where 子句顺序对 SQL 查询速度的影响

Effect of where clause order on SQL query speed

我遇到一个问题,在 SQL-Server 上,复杂查询 运行 慢得令人无法接受(每个事务大约 10 秒)。我不确定版本,但至少是 2005。 该查询将大约 40 个字段(列)与相应的变量进行匹配,并且 returns 那些所有字段都匹配或者字段和变量都为空的行。 至少前几个字段被索引。 就目前而言,查询中的第一个字段将匹配几乎每条记录。重新排序查询,以便首先放置不太可能匹配的字段,允许在发现不匹配时提前终止对给定行的查询? 还有什么其他的加速方法可以推荐吗?

重新命名变量的查询如下:

SELECT _pk FROM FinancialDetail 
WHERE   ( Field01 = @Field01 OR ( Field01 IS NULL AND @Field01 IS NULL ) )
    AND ( Field02 = @Field02 OR ( Field02 IS NULL AND @Field02 IS NULL ) )
    AND ( Field03 = @Field03 OR ( Field03 IS NULL AND @Field03 IS NULL ) )
    AND ( Field04 = @Field04 OR ( Field04 IS NULL AND @Field04 IS NULL ) )
    AND ( Field05 = @Field05 OR ( Field05 IS NULL AND @Field05 IS NULL ) )
    AND ( Field06 = @Field06 OR ( Field06 IS NULL AND @Field06 IS NULL ) )
    AND ( Field07 = @Field07 OR ( Field07 IS NULL AND @Field07 IS NULL ) )
    AND ( Field08 = @Field08 OR ( Field08 IS NULL AND @Field08 IS NULL ) )
    AND ( Field09 = @Field09 OR ( Field09 IS NULL AND @Field09 IS NULL ) )
    AND ( Field10 = @Field10 OR ( Field10 IS NULL AND @Field10 IS NULL ) )
    AND ( Field11 = @Field11 OR ( Field11 IS NULL AND @Field11 IS NULL ) )
    AND ( Field12 = @Field12 OR ( Field12 IS NULL AND @Field12 IS NULL ) )
    AND ( Field13 = @Field13 OR ( Field13 IS NULL AND @Field13 IS NULL ) )
    AND ( Field14 = @Field14 OR ( Field14 IS NULL AND @Field14 IS NULL ) )
    AND ( Field15 = @Field15 OR ( Field15 IS NULL AND @Field15 IS NULL ) )
    AND ( Field16 = @Field16 OR ( Field16 IS NULL AND @Field16 IS NULL ) )
    AND ( Field17 = @Field17 OR ( Field17 IS NULL AND @Field17 IS NULL ) )
    AND ( Field18 = @Field18 OR ( Field18 IS NULL AND @Field18 IS NULL ) )
    AND ( Field19 = @Field19 OR ( Field19 IS NULL AND @Field19 IS NULL ) )
    AND ( Field20 = @Field20 OR ( Field20 IS NULL AND @Field20 IS NULL ) )
    AND ( Field21 = @Field21 OR ( Field21 IS NULL AND @Field21 IS NULL ) )
    AND ( Field22 = @Field22 OR ( Field22 IS NULL AND @Field22 IS NULL ) )
    AND ( Field23 = @Field23 OR ( Field23 IS NULL AND @Field23 IS NULL ) )
    AND ( Field24 = @Field24 OR ( Field24 IS NULL AND @Field24 IS NULL ) )
    AND ( Field25 = @Field25 OR ( Field25 IS NULL AND @Field25 IS NULL ) )
    AND ( Field26 = @Field26 OR ( Field26 IS NULL AND @Field26 IS NULL ) )
    AND ( Field27 = @Field27 OR ( Field27 IS NULL AND @Field27 IS NULL ) )
    AND ( Field28 = @Field28 OR ( Field28 IS NULL AND @Field28 IS NULL ) )
    AND ( Field29 = @Field29 OR ( Field29 IS NULL AND @Field29 IS NULL ) )
    AND ( Field30 = @Field30 OR ( Field30 IS NULL AND @Field30 IS NULL ) )
    AND ( Field31 = @Field31 OR ( Field31 IS NULL AND @Field31 IS NULL ) )
    AND ( Field32 = @Field32 OR ( Field32 IS NULL AND @Field32 IS NULL ) )
    AND ( Field33 = @Field33 OR ( Field33 IS NULL AND @Field33 IS NULL ) )
    AND ( Field34 = @Field34 OR ( Field34 IS NULL AND @Field34 IS NULL ) )
    AND ( Field35 = @Field35 OR ( Field35 IS NULL AND @Field35 IS NULL ) )
    AND ( Field36 = @Field36 OR ( Field36 IS NULL AND @Field36 IS NULL ) )
    AND ( Field37 = @Field37 OR ( Field37 IS NULL AND @Field37 IS NULL ) )
    AND ( Field38 = @Field38 OR ( Field38 IS NULL AND @Field38 IS NULL ) )
    AND ( Field39 = @Field39 OR ( Field39 IS NULL AND @Field39 IS NULL ) )
    AND ( Field40 = @Field40 OR ( Field40 IS NULL AND @Field40 IS NULL ) )

评论有点长。我猜测 OR 条件混淆了优化器。你有一个复杂的(尽管是重复的)WHERE 条件。

一个很大的问题是SQL服务器缓存了执行计划,所以第一次查询是运行,计划就设置好了。如果以后的值可能会更好地利用索引,则它们不一定会被使用。

我推荐动态 SQL,以消除 OR 条件。结果条件将取决于变量的值。如果 @Field02@Field03NULL,那么条件看起来像:

WHERE ( Field01 = @Field01 ) AND
      ( Field02 IS NULL ) AND
      ( Field03 IS NULL )  AND
      ( Field04 = @Field04 ) AND
      . . .

这些是严格的 AND 相等条件,优化器应该更容易处理。

这不是动态搜索。正在比较所有列。在不更改语义的情况下无法删除任何内容。

Field01 = @Field01 OR ( Field01 IS NULL AND @Field01 IS NULL ) 

只是表示两个 Null 比较相等时两者相等。此模式通常不会导致 SQL 服务器出现任何问题。一个简化的例子是

CREATE TABLE #FinancialDetail
(
_pk INT PRIMARY KEY,
Field01 INT,
Field02 INT,
Field03 INT
)
CREATE INDEX IX ON #FinancialDetail(Field01, Field02, Field03)

declare @Field01 int, @Field02 int, @Field03 int;

SELECT _pk FROM #FinancialDetail 
WHERE   ( Field01 = @Field01 OR ( Field01 IS NULL AND @Field01 IS NULL ) )
    AND ( Field02 = @Field02 OR ( Field02 IS NULL AND @Field02 IS NULL ) )
    AND ( Field03 = @Field03 OR ( Field03 IS NULL AND @Field03 IS NULL ) )

这显示了对所有三列的索引查找

这个计划实际上与

的计划没有区别
SELECT _pk FROM #FinancialDetail 
WHERE   ( Field01 = @Field01 )
    AND ( Field02 = @Field02 )
    AND ( Field03 = @Field03 )

索引查找中的相等运算符对 NULL 使用不同语义的事实并未向我们公开 as discussed in the comments here

您需要在一些相当有选择性的列组合上建立复合索引(SQL 服务器最多允许 16 个键列)并且应该能够对其进行搜索。

如果您需要使用现有的单列索引并且最佳索引可能会有所不同,您可以添加 OPTION (RECOMPILE).

您也可以重写如下,更短且语义相同,但我怀疑这会对计划产生任何影响。

SELECT _pk FROM #FinancialDetail 
WHERE EXISTS (SELECT @Field01, @Field02, @Field03 /*.... , @Field40*/
              INTERSECT
              SELECT Field01, Field02, Field03 /*.... , Field40*/ )