查询所有行,其中至少一个子项满足约束1,并且所有子项满足MySQL中的约束2

问题描述 投票:0回答:2

我有一个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)

我希望有一种方法可以更快地编写这个查询,例如在一分钟或更短的时间。

mysql sql database query-optimization
2个回答
0
投票

我假设pickup_date列已编入索引。如果不是,那么无论你做什么,查询都会很慢。

要注意的是,大多数行都是历史记录(trip.pickup_date <'2019-04-02')。所以你想要的是一个查询(或子查询),它只选择最近的旅行,然后围绕它建立其余的查询。

你用内在的查询做到了这一点,所以我说有正确的想法。那么为什么它会变慢?要么pick_date没有编入索引,要么以一种混淆MySQL使用该索引的方式编写查询。 (MySQL的EXPLAIN command可以显示是否发生这种情况。)

有一些方法可以简化查询。一些:

  1. 我不认为内部查询需要加入拼车表 - 我不认为这会带来巨大的加速。
  2. 您可以尝试将整个事物编写为两个SQL语句,然后使用UNION。 (这也消除了一个OR,有时可以帮助。)
  3. 有办法摆脱GROUP BY ..有可能或可能没有帮助。

或者:在我看来,查询正在返回已完成的拼车,以及尚未开始的拼车。可能更简单的是测试中间的所有拼车(即拼车未完成;但至少有一次行程已获得状态或稍后。)如果您尝试此操作,请将结果与您的慢查询进行比较确定他们返回相同的结果。可能会有一些模糊的状态需要处理。


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

© www.soinside.com 2019 - 2024. All rights reserved.