假设我有一个包含 1000 条记录的表并且我有以下查询,它将返回 7 条记录:
SELECT *
FROM MyTable
WHERE IndexedColumn > 5000
AND OtherIndexedColumn = 2
由于这两列都有最新的索引,SQL Server 可以做出假设并更快地找到值,并且两个查询都是 SARGable(理想情况下读取次数更少)。但是,假设我需要确保该值不等于另一个值,假设不同的列为 12,那么我必须添加
AND AnotherIndexedColumn <> 12
如果那是WHERE
子句中的最后一条语句,SQL Server 是否使用前两个 WHERE 语句中的 SARGability 先过滤,得到 7 行,然后查看这 7 行中的每一行是否不等于 12,或者是否适用1000 行原始数据集中的<>
每一行?
我问的原因是因为我知道我可以使用子查询或 CTE 来执行 SARGable 过滤器的第一部分,然后在 7 行中,查看每行是否不相等,但是查询优化器已经在幕后做这件事,还是最好自己做?
理论上,尤其是在现代版本的 SQL Server 中,WHERE 子句的顺序绝对没有区别。SQL Server通常会以最有效的顺序处理过滤器,并会选择它确定的索引以进行最有效的查询。可能出现的差异是当 WHERE 子句包含 JOIN 条件或将过滤器应用于外部连接列时;当使用 FORCE ORDER 或特定跟踪标志等选项时,其他差异也会发挥作用。
重写为 CTE 以“强制”SQL Server 首先在 CTE 内处理过滤器是行不通的——SQL Server 无论如何都会重写它,并以它认为最好的顺序处理它。这就是为什么,例如,您不能通过使用 CTE 或子查询首先过滤掉坏数据来逃避无效转换错误——SQL Server 可能仍然会在任何过滤器执行任何操作之前将坏数据呈现给转换。
(Erland 在Connect #537419中对此进行了抱怨。)
所以,一般来说,不要重写你的 SQL 代码来试图超越优化器或防止它做一些愚蠢的事情。在某些情况下,它可能会选择错误的索引,这可能会通过更改查询本身来解决,但是在您已经认为优化器选择了错误的索引的情况下处理这些情况(在解决这些问题之前,消除更可能的原因,如参数嗅探、不良统计数据等)。