MySQL оптимизирует сложный запрос NOT IN (2 SELECT WITH JOIN) ГДЕ

У меня есть следующий запрос:

SELECT driver_id, first_name, last_name
 FROM drivers
 WHERE driver_id NOT IN 
 (SELECT DISTINCT w.driver_id from waybills w 
 JOIN drivers d ON d.driver_id = w.driver_id 
 WHERE w.waybill_owner = 1 
 AND w.waybill_status = 'dispatched'
 AND w.delivery_date = '2014-10-28')
 AND driver_id NOT IN 
 (SELECT DISTINCT wm.driver_id from waybill_movements wm 
 JOIN drivers d ON d.driver_id = wm.driver_id 
 WHERE wm.movement_owner = 1 
 AND wm.delivery_date = '2014-10-28')
 AND status = 'active'
 AND driver_owner = 1
 ORDER BY last_name ASC

Как я могу оптимизировать этот запрос?

Запрос работает хорошо и возвращает ожидаемые результаты, но мой вопрос в том, можно ли оптимизировать запрос.

Большое спасибо за ваше время и помощь.

Обновить:

И, да, у меня есть эти показатели:

waybill_owner, waybill_status, w.delivery_date) waybill_movements (wm.movement_owner, delivery_date) (driver_id - первичный ключ и драйверы (статус, driver_owner)

Структура таблицы не нужна для оптимизации

Я не ожидал, что будет так много ответов.Спасибо вам всем.

4 ответа

Обычно я обнаружил, что выполнение левого соединения и поиск NULL результата таблицы проще, а также большое использование индексов, которые могут быть сделаны.

The drivers table I would have an index ON (driver_owner, status, driver_id)
Your waybill table, index ON(waybill_owner, driver_id, delivery_date, waybill_status)
waybill_movements, index ON(movement_owner, driver_id, delivery_date )


SELECT 
 d1.driver_id, 
 d1.first_name, 
 d1.last_name
 FROM 
 drivers d1
 LEFT JOIN waybills w
 ON d1.driver_id = w.driver_id
 AND d1.driver_owner = w.waybill_owner
 AND w.waybill_status = 'dispatched'
 AND w.delivery_date = '2014-10-28'
 LEFT JOIN waybill_movements wm 
 ON d1.driver_id = wm.driver_id 
 AND d1.driver_owner = wm.movement_owner
 AND wm.delivery_date = '2014-10-28'
 where
 d1.driver_owner = 1
 AND d1.status = 'active'
 AND w.driver_ID IS NULL
 AND wm.driver_id IS NULL
 ORDER BY 
 d1.last_name ASC

Включая "IS NULL" для накладной и waybill_movements, вы получаете только те, у которых нет соответствующей записи.

Кроме того, видя, что ваш waybill_owner = 1... совпадает с совпадением с идентификатором driver_owner = 1? ЕСЛИ владелец waybill должен всегда быть таким же, как владелец драйвера, тогда я бы изменил индекс в таблице waybill, чтобы сначала иметь waybill_owner, затем driver_id, а затем остальные AND изменить соединения на основе drivers.driver_owner = waybills. waybill_owner (аналогично на waybill_movements)

ПЕРЕСМОТРЕННЫЙ за отзыв

Пересмотренные индексы и запрос объединяются на основе той же таблицы drivers.driver_owner в таблицы waybill, чтобы предотвратить ложные результаты совпадений с владельцами совпадений по любой странной возможности.


Это зависит от того, что определение is есть. Возможно, вы захотите переписать те WHERE IN в ваше предложение FROM, а затем запустите свой существующий запрос против этого, голова к голове и посмотрите, объясняет ли MySQL объяснение для другого пути выполнения и статистики для этого.

Несомненно, это будет так, потому что вы в значительной степени должны будете поразить свою таблицу drivers три раза в своем существующем запросе, чтобы получить нужные результаты. Это лишние накладные расходы.

SELECT d.driver_id, d.first_name, d.last_name
FROM drivers d
 LEFT OUTER JOIN waybills w ON
 d.driver_id = w.driver_id AND
 w.waybill_owner = 1 AND
 w.waybill_status = 'dispatched' AND
 w.delivery_date = '2014-10-28'
 LEFT OUTER JOIN waybill_movements wm ON
 d.driver_id = wm.driver_id AND
 wm.movement_owner = 1 AND
 wm.delivery_date = '2014-10-28')
WHERE
 w.driver_id IS NULL AND
 wm.driver_id IS NULL AND
 d.status = 'active' AND
 d.driver_owner = 1
ORDER BY last_name ASC

Ваш ORDER BY также дорогой, как и всегда. Если это не понадобится, может быть хорошей идеей удалить его.

Возможно, вы захотите запустить @StuartLC, предложенный SQL, и этот, объясняя и видя, что MySQL любит один из них лучше. Вы можете найти с ним лучшие результаты, но иногда это монета. MySQL, как правило, не очень быстро справляется с производными таблицами, поэтому, как можно больше вносятся в традиционное соединение, можно добиться лучших результатов. Но это полностью зависит от размера таблиц, независимо от того, имеют ли они надлежащие индексы и все эти забавные вещи.


Предполагая, что вы уже посмотрели на индексацию

  • waybills(waybill_owner, waybill_status, w.delivery_date)
  • waybill_movements (wm.movement_owner, delivery_date)
  • drivers(driver_id) - предположительно первичный ключ? и, возможно, также drivers(status, driver_owner)

Еще два улучшения

  • Ни один из подзапросов на самом деле не использует таблицу подключенных драйверов. Предполагая, что это намеренно, вы можете либо удалить соединение (если намерение не фильтровать накладные/движения, которые вообще не имеют никакого драйвера), либо просто отфильтровать, where driver_id IS NOT NULL если это это нулевой внешний ключ
  • Если вы объединяете два подзапроса, вы можете сделать один NOT IN
SELECT driver_id, first_name, last_name
 FROM drivers
 WHERE driver_id NOT IN 
 (
 SELECT DISTINCT w.driver_id
 from waybills w 
 WHERE w.waybill_owner = 1 
 AND w.waybill_status = 'dispatched'
 AND w.delivery_date = '2014-10-28'
 UNION
 SELECT DISTINCT wm.driver_id 
 from waybill_movements wm 
 WHERE wm.movement_owner = 1 
 AND wm.delivery_date = '2014-10-28'
 )
 AND status = 'active'
 AND driver_owner = 1
 ORDER BY last_name ASC


SELECT d.driver_id, d.first_name, d.last_name 
FROM drivers AS d
LEFT JOIN waybills AS w 
 ON d.driver_id = w.driver_id 
 AND w.waybill_owner = 1
 AND w.waybill_status = 'dispatched'
 AND w.delivery_date = '2014-10-28'
LEFT JOIN waybill_movements AS wm 
 ON d.driver_id = wm.driver_id
 AND wm.movement_owner = 1 
 AND wm.delivery_date = '2014-10-28'
WHERE w.driver_id IS NULL 
AND wm.driver_id IS NULL
AND d.status = 'active'
AND d.driver_owner = 1
ORDER BY d.last_name ASC

licensed under cc by-sa 3.0 with attribution.