优化的SQL查询比未优化的查询慢?

时间:2018-08-20 19:51:49

标签: mysql optimization benchmarking

一个程序员的同伴向我展示了他创建的查询,如下所示:

SELECT a.row, b.row, c.row
FROM 
a LEFT JOIN
b ON (a.id = b.id) LEFT JOIN
c ON (c.otherid= b.otherid)
WHERE a.id NOT IN (SELECT DISTINCT b.id bb 
INNER JOIN
c cc ON (bb.a_id = cc.a_id)
WHERE (bb.date BETWEEN '2018-08-04 00:00:00' AND '2018-08-06 23:59:59'))
GROUP BY a.id ORDER BY c.otherid DESC;

因此,我通过删除第二个查询并直接应用WHERE子句来缩短它:

SELECT a.row, b.row, c.row
FROM 
a LEFT JOIN
b ON (a.id = b.id) LEFT JOIN
c ON (c.otherid= b.otherid)
WHERE b.date NOT BETWEEN '2018-08-04 00:00:00' AND '2018-08-06 23:59:59'
GROUP BY a.id ORDER BY c.otherid DESC;

直到这里,一切似乎都很好,并且两个查询都返回相同的结果集。问题在于,第二个查询的执行时间是第一个查询的三倍。那怎么可能? 谢谢

1 个答案:

答案 0 :(得分:1)

查询明显不同。 (我们假设第一个版本的子查询中缺少FROM关键字是将其放入问题中的结果,并且原始查询没有相同的语法错误。此外,对子查询的b.id列表中的SELECT高度可疑,我们怀疑这确实是对bb.id的引用……但我们只是在猜测。)

如果两个查询返回的是完全相同的结果集,则说明数据存在这种情况。 (我们可以演示两个查询的结果不同的数据集。)

“缩短”查询并不一定会优化查询。

真正重要的是(在性能方面)执行计划。也就是说,正在执行什么操作,以什么顺序执行以及在大型表中哪些索引可用并且正在使用。

没有表和索引的定义,就不可能做出明确的诊断。

建议:使用MySQL EXPLAIN查看每个查询的执行计划。


假定原始查询具有以下形式的WHERE子句:

WHERE a.id NOT IN ( SELECT DISTINCT bb.id 
                      FROM b bb 
                      JOIN c cc
                        ON bb.a_id = cc.a_id
                     WHERE bb.date BETWEEN '2018-08-04 00:00:00' 
                                       AND '2018-08-06 23:59:59'
                       AND bb.id IS NOT NULL
                  )

(假设我们保证子查询返回的值永远不会为NULL ...)

可以将其重写为NOT EXISTS相关子查询,以实现等效结果:

  WHERE NOT EXISTS ( SELECT 1
                       FROM b bb
                       JOIN c cc
                         ON cc.a_id = bb.a_id
                      WHERE bb.date >= '2018-08-04 00:00:00'
                        AND bb.date <  '2018-08-07 00:00:00'
                        AND bb.id = a.id
                   )

或者可以将其重写为反联接

  LEFT 
  JOIN b bb 
    ON bb.id = a.id
   AND bb.date >= '2018-08-04 00:00:00'
   AND bb.date <  '2018-08-07 00:00:00'
  LEFT
  JOIN c cc
    ON cc.a_id = bb.a_id
 WHERE cc.a_id IS NULL

对于大型集合,需要适当的索引才能获得最佳性能。

问题中提出的重写不能保证返回等效结果。