【问题标题】:How to reach an index-seek on nullable columns?如何在可空列上实现索引查找?
【发布时间】:2015-01-26 01:08:34
【问题描述】:

让我们假设以下代码:

CREATE TABLE Foo (V1 INT NULL, V2 INT NOT NULL)
CREATE INDEX niFooV1V2 ON Foo(V1, V2)
DECLARE @searchForV1 INT = NULL
DECLARE @searchForV2 INT = 0

如何编写使用索引查找所有具有指定值 V1 和 V2 的行的查询?

基本上,有一个准确的索引,应该可以通过一次查找找到所有行。但无论我尝试什么,SqlServer 都不会使用索引的两列。

例如我尝试了以下方法:

SELECT * FROM Foo WITH(FORCESEEK) WHERE ((V1 IS NULL AND @searchForV1 IS NULL) OR (V1 IS NOT NULL AND @searchForV1 IS NOT NULL AND @searchForV1 = V1)) AND V2 = @searchForV2

在执行计划中,它为 V1 执行两个独立的索引搜索(一个为 null,一个为非 null),然后为 V2 应用一个过滤器。在我的例子中,第一个索引搜索返回大约 100.000 行,然后扫描 V2 以将结果减少到 100 行。这不是很有效。

解决此问题的另一种方法可能是 IF-Else-Construct,我根据 @searchForV1 是 NULL 还是 NOT NULL 编写两个独立的查询。但这似乎很尴尬……

还有其他选择吗?

【问题讨论】:

    标签: sql-server indexing sql-execution-plan


    【解决方案1】:

    好吧,原来SqlServer在这里有点笨。

    问题是我使用的过滤条件:

    ((V1 IS NULL AND @searchForV1 IS NULL) OR (V1 IS NOT NULL AND @searchForV1 IS NOT NULL AND @searchForV1 = V1))
    

    如果您删除两个“IS NOT NULL”检查,则索引搜索工作得非常好:

    ((V1 IS NULL AND @searchForV1 IS NULL) OR @searchForV1 = V1)
    

    虽然这两种检查在语义上是等效的,但 SqlServer 仅在后一种情况下使用索引搜索。在编写代码时,为了安全起见,我只是添加了两个“IS NOT NULL”检查,因为将空值与非空值进行比较时产生的未知结果在否定整体时通常会导致问题东西。

    感谢 Dan 让我检查这个。

    【讨论】:

    • 你打败了我。我同意 SQL Server 可以更聪明地消除冗余谓词。
    【解决方案2】:

    尝试反转索引列顺序并删除 FORCESEEK 提示:

    CREATE INDEX dbo.niFooV2V1 ON Foo(V2, V1);
    
    SELECT  *
    FROM    Foo
    WHERE   ( ( V1 IS NULL
                AND @searchForV1 IS NULL
              )
              OR ( V1 IS NOT NULL
                   AND @searchForV1 IS NOT NULL
                   AND @searchForV1 = V1
                 )
            )
            AND V2 = @searchForV2;
    

    此索引将允许在 V2 相等谓词上进行索引查找。索引的第一列应首先指定查询中使用的等式谓词,其中最有选择性的列。

    从性能的角度来看这并不重要,但查询可以简化如下,因为 V1 和 @searchForV1 IS NULL 条件由于相等条件是多余的。

    SELECT  *
    FROM    Foo
    WHERE   ( ( V1 IS NULL
                AND @searchForV1 IS NULL
              )
              OR @searchForV1 = V1
            )
            AND V2 = @searchForV2;
    

    【讨论】:

    • 感谢您的回答。这是一个很好的解决方法,但一旦两列都可以为空,就会失败。
    猜你喜欢
    • 2013-08-06
    • 1970-01-01
    • 2011-05-06
    • 1970-01-01
    • 2013-02-02
    • 2011-02-11
    • 1970-01-01
    • 2021-03-21
    • 2017-12-18
    相关资源
    最近更新 更多