一个程序员的同伴向我展示了他创建的查询,如下所示:
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;
直到这里,一切似乎都很好,并且两个查询都返回相同的结果集。问题在于,第二个查询的执行时间是第一个查询的三倍。那怎么可能? 谢谢
答案 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
对于大型集合,需要适当的索引才能获得最佳性能。
问题中提出的重写不能保证返回等效结果。