我正在重写不再提取所有必需数据的查询。我的问题是关于我从未见过的一种做法,也没有在 StackExchange 上找到任何专门解决该问题的问题。
我知道该HAVING
语句的重点是在聚合上引入条件,就像WHERE
在单个行上引入条件一样。但是,我在这段代码中看到的内容HAVING
被用来代替WHERE
聚合查询。中的条件HAVING
不应用于聚合,而是应用于非聚合列。
例如:
SELECT id, filedate, SUM(amount)
FROM Sales
GROUP BY id, filedate
HAVING id = 123 AND filedate = '1/1/2018'
相对于:
SELECT id, filedate, SUM(amount)
FROM Sales
WHERE id = 123 AND filedate = '1/1/2018'
GROUP BY id, filedate
此策略是否存在性能影响或其他优点/缺点?
我没有尝试自己运行诊断程序,这不是优先事项,我必须自己做。但是,如果对此没有明确的答案,我想我可能会。
我关心的是优化器如何查看这个查询。它是聚合所有数据,然后根据HAVING
子句限制结果集,还是意识到它可以对各个行应用具有条件,因为它们专门引用非聚合列?
编辑:对于我的示例查询和我正在重写的实际 SQL,计划是相同的,但查询具有相似的复杂性,我还没有足够的知识从相同的计划中得出结论。
这里的问题在于您如何描述该
HAVING
条款适用的内容。该HAVING
子句始终适用于聚合字段,即聚合后的所有剩余列。您试图表明/说该HAVING
子句未应用于任何聚合函数,这是它们通常适用的。但实际上,该HAVING
子句控制该聚合函数的结果,或者在您的第一个示例中,控制分组列的结果。但在这两种情况下,聚合已经执行。因此,在性能方面(更不用说其他人稍后尝试更新此代码的可读性),您使用
WHERE
子句过滤到将要聚合的内容,然后使用HAVING
子句过滤掉已经聚合的内容聚合。并且,虽然问题中显示的简单测试的结果掩盖了两者时间之间的差异(或查询处理顺序中的逻辑位置),使得它们“看起来”在做同样的事情,如果聚合一堆行只是为了稍后将它们扔掉而在逻辑上它们本可以在存储/计算聚合之前被消除时并没有降低效率,我会感到非常惊讶。但是,如果您确实看到这个简单示例的执行计划相似,我敢打赌,这仅仅是因为优化器认为将这些HAVING
条件变为现实会更有效WHERE
条件,因为它在执行之前重写查询。但在这种情况下,我仍然建议不要以这种方式编写查询,因为你会让优化器花费额外的时间来重写错误的代码,而它应该花费时间/CPU 周期来寻找更有效的计划。@DavidSpillett补充说(在对此答案的评论中):“此外,您依赖查询规划器看到优化潜力,它可能不会在更复杂的查询中或者如果您的代码最终移植到另一个数据库(甚至只是一个旧版本的 SQL Server)”。对于它的价值,即使是 HAVING 子句的 Microsoft文档也表示它
WHERE
在没有出现时充当子句GROUP BY
。现在文档在 GitHub 上,我最近能够通过Pull Request #235: Correct and Improvement HAVING 子句对其进行更正。Solomon 给出了很好的解释,但对我来说,简单的答案是记住 Itzik Ben-Gan 在这里写的 SQL 查询逻辑处理顺序 顺序总是
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY
所以你看,如果我们可以在 GROUP BY 之前应用 WHERE 过滤器,我们可能会减少 GROUP BY 处理的数据量,尤其是当存在适当的索引时,WHERE 操作会非常有效。因此,我想说如果使用 WHERE 和 HAVING 从业务角度返回相同的结果,那么 WHERE 总是胜过 HAVING。