我有一个MySQL数据库,其中包含表示可能的拼车路线的表格。三个相关的表是拼车表(基数~2百万),carpool_stop表(基数~1,100万)和旅行表(基数~300K)。旅程表示从位置A移动到位置B的请求。拼车代表汽车可以通过在多个位置拾取用户并在多个位置丢弃它们来一次完成多次旅行的可能路线。以下是示例:拼车:
+------------+-----------+
| carpool_id | completed |
+------------+-----------+
| 1 | 0 |
| 2 | 0 |
| 3 | 1 |
+------------+-----------+
carpool_stop:
+------------+---------+---------+
| 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.由于旅程3已经是拼车的一部分并且是'IN_PROGRESS',因此不会返回拼车2。
我有一个工作查询,但由于carpool_stop表中的行数,现在需要10分钟才能完成指定的pickup_date,这只是过去的一天。
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)
我希望有一种方法可以更快地编写这个查询,例如在一分钟或更短的时间。
我假设pickup_date列已编入索引。如果不是,那么无论你做什么,查询都会很慢。
要注意的是,大多数行都是历史记录(trip.pickup_date <'2019-04-02')。所以你想要的是一个查询(或子查询),它只选择最近的旅行,然后围绕它建立其余的查询。
你用内在的查询做到了这一点,所以我说有正确的想法。那么为什么它会变慢?要么pick_date没有编入索引,要么以一种混淆MySQL使用该索引的方式编写查询。 (MySQL的EXPLAIN
command可以显示是否发生这种情况。)
有一些方法可以简化查询。一些:
或者:在我看来,查询正在返回已完成的拼车,以及尚未开始的拼车。可能更简单的是测试中间的所有拼车(即拼车未完成;但至少有一次行程已获得状态或稍后。)如果您尝试此操作,请将结果与您的慢查询进行比较确定他们返回相同的结果。可能会有一些模糊的状态需要处理。
仅基于标题:
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
。