我有一个MySQL数据库,其中的表表示可能的拼车路线。这三个相关的表是拼车表(基数约为200万),拼车停车表(基数约为1100万)和行程表(基数约为300K)。行程代表从位置A移至位置B的请求。拼车代表汽车通过在多个位置接送用户并将其在多个位置下车而一次完成多个行程的可能路线。以下是示例: 拼车:
+------------+-----------+
| carpool_id | completed |
+------------+-----------+
| 1 | 0 |
| 2 | 0 |
| 3 | 1 |
+------------+-----------+
拼车停车:
+------------+---------+---------+
| carpool_id | trip_id | type |
+------------+---------+---------+
| 1 | 1 | pickup |
| 1 | 2 | pickup |
| 1 | 2 | dropoff |
| 1 | 1 | dropoff |
| 2 | 2 | pickup |
| 2 | 3 | pickup |
| 2 | 3 | dropoff |
| 2 | 2 | dropoff |
| 3 | 3 | pickup |
| 3 | 4 | pickup |
| 3 | 4 | dropoff |
| 3 | 3 | dropoff |
+------------+---------+---------+
行程:
+---------+------------+---------------+--------------+
| trip_id | carpool_id | status | pickup_date |
+---------+------------+---------------+--------------+
| 1 | NULL | 'INITIAL' | '2019-04-01' |
| 2 | NULL | 'INITIAL' | '2019-04-02' |
| 3 | 3 | 'IN_PROGRESS' | '2019-04-03' |
| 4 | 3 | 'INITIAL' | '2019-04-03' |
+---------+------------+---------------+--------------+
trip.pickup_date有一个索引。 目标是获得所有满足以下条件的拼车:
at least one trip has a pickup_date later than a specified date
AND
(the carpool is completed OR
(all trips have status in ('INITIAL', 'WAITING') AND have a NULL carpool_id))
在上面的示例中,如果指定的pickup_date为“ 2019-04-02”,则将是拼车1和拼车3。拼车2将不会返回,因为行程3已经是拼车的一部分并且是“ IN_PROGRESS”。
我有一个有效的查询,但是由于carpool_stop表中的行数众多,所以对于过去的一天,指定的领取日期需要10分钟才能完成。
SELECT carpool.*
FROM (
SELECT carpool_stop.carpool_id
FROM trip
JOIN carpool_stop ON carpool_stop.trip_id = trip.trip_id
JOIN carpool ON carpool.carpool_id = carpool_stop.carpool_id
WHERE trip.pickup_date >= '2019-04-02'
GROUP BY carpool.carpool_id
) AS inner_query
JOIN carpool ON carpool.carpool_id = inner_query.carpool_id
JOIN carpool_stop ON carpool_stop.carpool_id = carpool.carpool_id
JOIN trip ON trip.trip_id = carpool_stop.trip_id
GROUP BY carpool.carpool_id
HAVING (sum(CASE WHEN (trip.status NOT IN ('INITIAL', 'WAITING') OR trip.carpool_id IS NOT NULL)
THEN 1
ELSE 0
END) = 0
OR carpool.completed = 1)
我希望有一种方法可以更快地编写此查询,例如一分钟或更短的时间。
答案 0 :(得分:0)
我假设已为picking_date列建立索引。如果不是,那么无论您做什么查询都会很慢。
要记住的主要事情是,大多数行都是历史记录(trip.pickup_date <'2019-04-02')。因此,您想要的是一个仅选择最近行程的查询(或子查询),然后围绕该查询构建其余查询。
您只是通过内部查询做到了这一点,所以我想有一个正确的主意。那为什么慢呢?没有为picking_date编制索引,或者以使MySQL无法使用该索引的方式编写查询。 (MySQL的EXPLAIN
command可以显示这种情况是否发生。)
有多种方法可以简化查询。只是几个:
或者:在我看来,该查询返回的是已完成的拼车,以及尚未开始的拼车。相反,测试中间的所有拼车可能会更简单(即拼车尚未完成;但是至少有一个行程的状态已被选择或稍后)。如果尝试此操作,请将结果与慢速查询进行比较,以得出确保它们返回相同的结果。可能有些模糊的状态需要处理。
答案 1 :(得分:0)
仅基于标题:
SELECT ...
FROM ...
WHERE EXISTS( SELECT 1 FROM ... WHERE ... ) -- at least 1 child
AND NOT EXISTS( SELECT 1 FROM ... WHERE NOT ... ) -- all (ie, none fail)
如果需要帮助,请提供SHOW CREATE TABLE
。