MySQL优化复杂查询NOT IN(2 SELECT WITH JOIN)WHERE

时间:2014-10-28 13:20:42

标签: mysql select 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) drivers(driver_id主键和驱动程序(status,driver_owner)

表格结构不需要它进行优化

我没想到会有这么多答案。谢谢大家。

4 个答案:

答案 0 :(得分:2)

这取决于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通常不是非常快速,因此尽可能多地填充传统连接可以产生更好的结果。但这完全取决于表格的大小,是否有适当的索引和所有有趣的东西。

答案 1 :(得分:1)

假设您已经查看了索引

  • waybills(waybill_owner, waybill_status, w.delivery_date)
  • waybill_movements (wm.movement_owner, delivery_date)
  • drivers(driver_id) - 可能是主键?还可能drivers(status, driver_owner)

其他两项改进让人想起

  • 两个子查询都没有实际使用Joined驱动程序表。假设这是故意的,你可以删除联接(如果意图不是过滤没有任何驱动程序的运单/动作),或者只是过滤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

答案 2 :(得分:1)

我通常发现执行left-joins并查找表结果为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

通过为运单和waybill_movements添加“IS NULL”,您只能获得那些没有匹配记录的那些。

另外,看到你的waybill_owner = 1 ......与driver_owner = 1的巧合也是一致的吗?如果运单所有者应始终与驱动程序所有者相同,那么我会将运单表上的索引更改为首先使用waybill_owner,然后更改driver_id,然后将其余部分更改为基于drivers.driver_owner =运单的连接。 waybill_owner(类似于waybill_movements)

根据反馈修改

根据相同的drivers.driver_owner表修改索引和要加入的查询到运单表,以防止可能存在任何奇怪的可能性导致跨所有者匹配的错误结果。

答案 3 :(得分:0)

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