Sql-Server

如果列具有 NOT NULL 約束,查詢優化器是否能夠優化 IS NOT NULL 條件?

  • September 1, 2020

我正在嘗試找出方法來提高非常慢的查詢的性能。它有更明顯的問題,但我注意到的一件事是 WHERE 子句中的條件之一是 ‘AND t.data IS NOT NULL’ 而表 t 的 ‘data’ 列沒有為 NULL 的條目並且確實有一個 NOT NULL 約束。

所以我想知道查詢優化器是否能夠忽略該條件。我的想法是,它不能僅僅因為約束而做到這一點(因為不能保證它是否是使用 NOVALIDATE 創建的),但可能足夠“聰明”以使用有關列中 NULL 欄位數量的統計資訊。

我自己的測試沒有定論,我無法找到有關此主題的任何進一步資訊。

我有限的測試表明,如果出現以下情況,可以消除“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

它有兩列:一列被聲明為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');

SSMS 結果視窗的螢幕截圖顯示約束是受信任和啟用的

然後我們可以得到關於這兩個查詢的估計計劃:

SELECT * FROM dbo.Test WHERE DeclareNotNull IS NOT NULL;
SELECT * FROM dbo.Test WHERE DeclaredNull IS NOT NULL;

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');

顯示約束不再受信任的 SSMS 結果視窗的螢幕截圖

我們得到了第二個查詢的估計計劃:

圖形執行計劃的螢幕截圖顯示掃描運算符中現在有一個謂詞

聚集索引掃描運算符現在包括一個“謂詞”部分,因為檢查約束不受信任。

引用自:https://dba.stackexchange.com/questions/274769