寻求不使用所有可用列的谓词

8 sql-server sql-server-2008-r2

我有一个奇怪的查询编译问题,很难重现。它只发生在高负载下,不能轻易重复。

  • 有一个包含 A、B、C、D 列的表 T。
  • T(A, B, C, D) 上有一个非唯一聚集索引。
  • 有一个查询 SELECT * FROM T WHERE A=@P1 AND B=@P2 AND (C=@P3 OR C=@P4) AND D=@P5。查找条件在聚集索引的所有列上,第 3 列有一个 OR。

问题是这个查询的查询计划只有在 A 和 B 上有 Seek Predicate!C 和 D 上的谓词是一个普通的谓词,所以这意味着 C 和 D 列上的搜索树没有被使用。

所有参数的数据类型都与列数据类型相匹配。

任何人都可以提供有关为什么会发生这种情况的任何提示吗?SQL 版本为 2008 R2 (SP1) - 10.50.2789.0 (X64)

Mar*_*ith 8

对于参数化查询它不能只做两次搜索

WHERE A=@P1 AND B=@P2 AND C=@P3 AND D=@P5 
Run Code Online (Sandbox Code Playgroud)

WHERE A=@P1 AND B=@P2 AND C=@P4 AND D=@P5 
Run Code Online (Sandbox Code Playgroud)

因为如果@P3 = @P4那会错误地带回重复的行。所以它需要一个首先从这些中删除重复项的操作员。

从这个快速测试来看,它似乎取决于表格的大小,无论你是否得到它。在下面的测试中,245/ 246rows 是计划之间的分界点(这也是将所有内容都放在一页上的索引与变成 2 个叶页和一个根页之间的分界点)。

CREATE TABLE T(A INT,B INT,C INT,D INT)

INSERT INTO T
SELECT TOP (245) 1,2,3,5
FROM master..spt_values v1

CREATE CLUSTERED INDEX IX ON T(A, B, C, D)

SELECT index_level,page_count, record_count
FROM sys.dm_db_index_physical_stats(db_id(),object_id('T'),1,NULL, 'DETAILED')

DECLARE @C1 INT = 3,
        @C2 INT = 4

 SELECT * FROM T WHERE A=1 AND B=2 AND (C=@C1 OR C=@C2) AND D=5

 DROP TABLE T
Run Code Online (Sandbox Code Playgroud)

1 页/245 行

这个计划有一个A=1 AND B=2带有残差谓词的搜索(C=@C1 OR C=@C2) AND D=5

方案一

2 叶页/246 行

方案二

在第二个计划中,额外的操作员负责@C1,@C2在执行搜索之前从第一个中删除任何重复项。

第二个计划中的搜索实际上是在A=1 AND B=2 AND C > Expr1010和之间的范围搜索,并A=1 AND B=2 AND C < Expr1011带有残差谓词 on D=5。它仍然不是对所有 4 列的平等寻求。可以在此处找到有关附加计划运营商的更多信息。

添加OPTION (RECOMPILE)确实允许它在编译时检查重复的参数值,并生成一个具有两个相等搜索的计划。

你也可以用

;WITH CTE
     AS (SELECT DISTINCT ( C )
         FROM   (VALUES (@C1),
                        (@C2)) V(C))
SELECT CA.*
FROM   CTE
       CROSS APPLY (SELECT *
                    FROM   T
                    WHERE A=1 AND B=2 AND D=5  AND C = CTE.C) CA
Run Code Online (Sandbox Code Playgroud)

计划3

但实际上在这个测试案例中,它可能会适得其反,因为对单页索引进行两次查找而不是一次查找会增加逻辑 IO。

  • 是的。OP 甚至指定密钥是非唯一的。幸运的是,我没有尝试回答这个问题:)。 (2认同)