如果列具有 NOT NULL 约束,查询优化器是否能够优化掉 IS NOT NULL 条件?

Flo*_*ian 10 sql-server constraint optimization sql-server-2016

我正在尝试找出提高非常慢查询性能的方法。它有更明显的问题,但我注意到的一件事是 WHERE 子句中的条件之一是 'AND t.data IS NOT NULL' 而表 t 的 'data' 列没有为 NULL 的条目并且确实有一个 NOT NULL 约束。

所以我想知道查询优化器是否能够忽略条件。我的想法是,它不能仅仅因为约束而这样做(因为不能保证它是否是用 NOVALIDATE 创建的),但可能足够“聪明”以使用有关列中 NULL 字段数量的统计信息。

我自己的测试是不确定的,我无法找到有关此主题的任何进一步信息。

Jos*_*ell 18

我的有限测试表明,在以下情况下可以消除“IS NOT NULL”谓词:

  • 该列NOT NULL在表定义中声明,或
  • 该列受到活动的、可信的检查约束的保护,不受空值影响

这是一个简单的测试表:

CREATE TABLE dbo.Test
(
    Id int IDENTITY(1,1) NOT NULL,
    DeclareNotNull int NOT NULL,
    DeclaredNull int NULL,

    CONSTRAINT PK_Test PRIMARY KEY (Id),
    CONSTRAINT CK_DeclaredNull CHECK (DeclaredNull IS NOT NULL)
);
GO

INSERT INTO dbo.Test
    (DeclareNotNull, DeclaredNull)
SELECT
    v.[number],
    v.[number]
FROM master.dbo.spt_values v
WHERE
    v.[number] IS NOT NULL;
GO
Run Code Online (Sandbox Code Playgroud)

它有两列:一列被声明为NOT NULL,另一列被声明NULL但有一个检查约束。两列都没有任何带有NULL值的行。

我们可以像这样验证检查约束:

SELECT 
    cs.[name],
    cs.[type_desc],
    cs.is_disabled,
    cs.is_not_trusted
FROM sys.check_constraints cs 
WHERE cs.parent_object_id = OBJECT_ID(N'dbo.Test');
Run Code Online (Sandbox Code Playgroud)

显示约束受信任并启用的 SSMS 结果窗口的屏幕截图

然后我们可以得到关于这两个查询的估计计划:

SELECT * FROM dbo.Test WHERE DeclareNotNull IS NOT NULL;
SELECT * FROM dbo.Test WHERE DeclaredNull IS NOT NULL;
Run Code Online (Sandbox Code Playgroud)

SSMS 中图形执行计划的屏幕截图显示扫描中没有谓词

请注意,扫描中没有“Predicate”或“Seek Predicate”部分,执行计划中也没有其他过滤器运算符。在这两种情况下都删除了空检查。

如果我们禁用检查约束:

ALTER TABLE dbo.Test
NOCHECK CONSTRAINT CK_DeclaredNull;
GO

SELECT 
    cs.[name],
    cs.[type_desc],
    cs.is_disabled,
    cs.is_not_trusted
FROM sys.check_constraints cs 
WHERE cs.parent_object_id = OBJECT_ID(N'dbo.Test');
Run Code Online (Sandbox Code Playgroud)

显示约束不再受信任的 SSMS 结果窗口的屏幕截图

我们得到了第二个查询的估计计划:

图形执行计划的屏幕截图,显示扫描运算符中现在有一个谓词

聚集索引扫描运算符现在包括一个“谓词”部分,因为检查约束不受信任。