Запрос для всех строк, где хотя бы один дочерний элемент удовлетворяет ограничению 1 и все дочерние элементы удовлетворяют ограничению 2 в MySQL - PullRequest
0 голосов
/ 04 апреля 2019

У меня есть база данных MySQL с таблицами, представляющими возможные маршруты автобуса.Тремя соответствующими таблицами являются таблица carpool (количество элементов ~ 2 миллиона), таблица carpool_stop (число элементов ~ 11 миллионов) и таблица поездок (количество элементов ~ 300K).Поездки представляют собой запрос на перемещение из местоположения A в местоположение B. Автомобильные бассейны представляют собой возможный маршрут, по которому автомобиль может совершить несколько поездок одновременно, подбирая пользователей в нескольких местах и ​​отбрасывая их в нескольких местах.Вот примеры: carpool:

+------------+-----------+
| 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:

+---------+------------+---------------+--------------+
| 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.Цель состоит в том, чтобы получить все carpools, которые удовлетворяют этим условиям:

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', это были бы carpools 1 и 3. Carpool 2 будетне возвращается, так как поездка 3 уже является частью автобазы и имеет значение IN_PROGRESS.

У меня есть рабочий запрос, но теперь он занимает 10 минут для указанной даты pickup_date, которая была только днем ​​в прошлом, потому чтоколичества строк в таблице carpool_stop.

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)

Я надеюсь, что способ написать этот запрос будет намного быстрее, например, порядка минуты или меньше.

Ответы [ 2 ]

0 голосов
/ 21 апреля 2019

На основании только названия:

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, если вам нужна помощь в применении его к вашим данным.

0 голосов
/ 04 апреля 2019

Я предполагаю, что столбец pickup_date проиндексирован. Если это не так, то независимо от того, что вы делаете, запрос будет медленным.

Главное помнить, что большинство строк - это история (trip.pickup_date <'2019-04-02'). Итак, вам нужен запрос (или подзапрос), который выбирает только последние поездки, а затем строит остальную часть запроса вокруг этого. </p>

Вы сделали именно это со своим внутренним запросом, поэтому я бы сказал, что правильно понял. Так почему же это медленно? Либо pickup_date не проиндексирован, либо ваш запрос написан так, что MySQL сбивает с толку использование этого индекса. (MySQL EXPLAIN команда может показать, если это происходит.)

Есть способы упростить запрос. Всего несколько:

  1. Я не думаю, что внутренний запрос должен присоединяться к таблице carpool - хотя я не ожидаю значительного ускорения от этого.
  2. Вы можете попробовать записать все это в виде двух операторов SQL, а затем использовать UNION. (Это также избавляет от ИЛИ, которое иногда может помочь.)
  3. Есть способы избавиться от GROUP BY .. ИМЕЯ, которые могут или не могут помочь.

В качестве альтернативы: мне кажется, что запрос возвращает завершенные carpools, а также еще не начавшиеся. Вместо этого может быть проще протестировать все промежуточные автомобильные бассейны (т. Е. Автомобильный пул не завершен; но по крайней мере одна поездка получила статус или позже). Если вы попробуете это, сравните результаты с медленным запросом, чтобы сделать уверен, что они возвращают одинаковые результаты. Там могут быть некоторые неясные статусы для обработки.

...