SQL性能问题:找到一个路由

时间:2018-06-15 01:00:26

标签: mysql sql mariadb

我在我的SQL查询中遇到性能问题

我有一个火车旅行,有5个车站,名为" A - B - C - D - E"。 乘客预订的门票仅为" B - C - D"骑。 我需要检索乘客前往的所有车站。

我存储的内容:

JOURNEY
+----+--------------------+-------------------+-------------------+-----------------+
| id | departure_datetime | arrival_datetime  | departure_station | arrival_station |
+----+--------------------+-------------------+-------------------+-----------------+
|  1 | 2018-01-01 06:00   | 2018-01-01 10:00  | A                 | E               |
+----+--------------------+-------------------+-------------------+-----------------+

BOOKING
+----+------------+-------------------+-----------------+
| id | journey_id | departure_station | arrival_station |
+----+------------+-------------------+-----------------+
|  1 |          1 | B                 | D               |
+----+------------+-------------------+-----------------+

LEG
+----+------------+-------------------+-----------------+------------------+------------------+
| id | journey_id | departure_station | arrival_station |  departure_time  |   arrival_time   |
+----+------------+-------------------+-----------------+------------------+------------------+
|  1 |          1 | A                 | B               | 2018-01-01 06:00 | 2018-01-01 07:00 |
|  2 |          1 | B                 | C               | 2018-01-01 07:00 | 2018-01-01 08:00 |
|  3 |          1 | C                 | D               | 2018-01-01 08:00 | 2018-01-01 09:00 |
|  4 |          1 | D                 | E               | 2018-01-01 09:00 | 2018-01-01 10:00 |
+----+------------+-------------------+-----------------+------------------+------------------+

我找到检索电台的唯一方法是:

select b.id as booking, l.departure_station, l.arrival_station
from JOURNEY j
inner join BOOKING b on j.id = b.journey_id
inner join LEG dl on (j.id = dl.journey_id and b.departure_station = dl.departure_station)
inner join LEG al on (j.id = al.journey_id and b.arrival_station = al.arrival_station)
inner join LEG l on (j.id = l.journey_id and l.departure_time >= dl.departure_time and l.arrival_time <= al.arrival_time)
where b.id = 1

但我的LEG表非常庞大,并且这3个连接非常慢。有没有办法只加入一次LEG表来提高性能?

预期回报:

+------------+-------------------+-----------------+
| booking_id | departure_station | arrival_station |
+------------+-------------------+-----------------+
|          1 | B                 | C               |
|          1 | C                 | D               |
+------------+-------------------+-----------------+

我在mariadb 12.2上工作,所以我可以访问窗口功能,但我仍然不太满意。

感谢。

编辑:创建表格:

CREATE TABLE `BOOKING` (
    `id` INT(11) NOT NULL,
    `journey_id` INT(11) NULL DEFAULT NULL,
    `departure_station` VARCHAR(50) NULL DEFAULT NULL,
    `arrival_station` VARCHAR(50) NULL DEFAULT NULL,
    PRIMARY KEY (`id`)
);

CREATE TABLE `JOURNEY` (
    `id` INT(11) NOT NULL AUTO_INCREMENT,
    `departure_time` DATETIME NULL DEFAULT NULL,
    `arrival_time` DATETIME NULL DEFAULT NULL,
    `departure_station` VARCHAR(50) NULL DEFAULT NULL,
    `arrival_station` VARCHAR(50) NULL DEFAULT NULL,
    PRIMARY KEY (`id`)
);

CREATE TABLE `LEG` (
    `id` INT(11) NOT NULL,
    `journey_id` INT(11) NULL DEFAULT NULL,
    `departure_station` VARCHAR(50) NULL DEFAULT NULL,
    `arrival_station` VARCHAR(50) NULL DEFAULT NULL,
    `departure_time` DATETIME NULL DEFAULT NULL,
    `arrival_time` DATETIME NULL DEFAULT NULL,
    PRIMARY KEY (`id`)
);

3 个答案:

答案 0 :(得分:4)

我不喜欢您的数据库架构。 但在您的特定情况下,因为您的查询对您有益。 我只是创建几个索引,加快执行速度。 一般来说,当你需要将表连接几次时,没有任何错误。

http://sqlfiddle.com/#!9/1a467/1

尝试添加4个索引:

CREATE INDEX journey ON BOOKING (journey_id);
CREATE INDEX arrival ON LEG (journey_id, arrival_station);
CREATE INDEX departure ON LEG (journey_id, departure_station);
CREATE INDEX d_a_time ON LEG (journey_id, departure_time, arrival_time);

再次运行查询,使用索引时应该快得多。

答案 1 :(得分:4)

我建议使用Common Table Expression (CTE)

WITH leg_cte as
(
    SELECT l.* FROM leg l
        JOIN booking b
            ON l.journey_id = b.journey_id
    WHERE b.id = 1
)

SELECT
    b.id as booking, 
    l.departure_station, 
    l.arrival_station
FROM
    booking b
    JOIN leg_cte dl
        ON b.departure_station = dl.departure_station
    JOIN leg_cte al 
        ON b.arrival_station = al.arrival_station
    JOIN leg_cte l 
        ON l.departure_time >= dl.departure_time AND l.arrival_time <= al.arrival_time

WHERE b.id = 1

答案 2 :(得分:2)

试一试left join并使用REGEXP来过滤发送号码和arrival_station

select T3.id booking_id , T1.departure_station,T1.arrival_station
from LEG T1
left join JOURNEY T2 on T1.`journey_id` = T2.`id`
  and (T1.`departure_time` >= T2.`departure_datetime` and T1.`arrival_time` <= T2.`arrival_datetime`)
left join BOOKING T3 on  T3.`id` = T2.`id` 
    and T1.departure_station REGEXP (CONCAT('[',T3.departure_station , '-' , T3.arrival_station,']' ))
    and T1.arrival_station REGEXP (CONCAT('[',T3.departure_station , '-' , T3.arrival_station,']' ))
where T1.journey_id = 1 and T3.id is not null ;

SQL Fiddle Demo Link

| booking_id | departure_station | arrival_station |
|------------|-------------------|-----------------|
|          1 |                 B |               C |
|          1 |                 C |               D |

测试DDL:

CREATE TABLE JOURNEY
    (`id` int, `departure_datetime` datetime, `arrival_datetime` datetime, `departure_station` varchar(1), `arrival_station` varchar(1))
;

INSERT INTO JOURNEY
    (`id`, `departure_datetime`, `arrival_datetime`, `departure_station`, `arrival_station`)
VALUES
    (1, '2018-01-01 06:00:00', '2018-01-01 10:00:00', 'A', 'E')
;


CREATE TABLE BOOKING
    (`id` int, `journey_id` int, `departure_station` varchar(1), `arrival_station` varchar(1))
;

INSERT INTO BOOKING
    (`id`, `journey_id`, `departure_station`, `arrival_station`)
VALUES
    (1, 1, 'B', 'D')
;


CREATE TABLE LEG
    (`id` int, `journey_id` int, `departure_station` varchar(1), `arrival_station` varchar(1), `departure_time` datetime, `arrival_time` datetime)
;

INSERT INTO LEG
    (`id`, `journey_id`, `departure_station`, `arrival_station`, `departure_time`, `arrival_time`)
VALUES
    (1, 1, 'A', 'B', '2018-01-01 06:00:00', '2018-01-01 07:00:00'),
    (2, 1, 'B', 'C', '2018-01-01 07:00:00', '2018-01-01 08:00:00'),
    (3, 1, 'C', 'D', '2018-01-01 08:00:00', '2018-01-01 09:00:00'),
    (4, 1, 'D', 'E', '2018-01-01 09:00:00', '2018-01-01 10:00:00')
;