SQL Server里的INTERSECT


在今天的文章里,我想討論下SQL Server里的INTERSECT設置操作。INTERSECT設置操作彼此交叉2個記錄集,返回2個集里列值一樣的記錄。下圖演示了這個概念。

 

INTERSECT與INNER JOIN

你會發現,它和2個表間的INNER JOIN幾乎一樣。但今天我會介紹它們之間的一些重要區別。讓我們從創建作為輸入的2個簡單表開始。 

 1 -- Create the 1st table
 2 CREATE TABLE t1
 3 (
 4     Col1 INT,
 5     Col2 INT,
 6     Col3 INT
 7 )
 8 GO
 9 
10 -- Create the 2nd table
11 CREATE TABLE t2
12 (
13     Col1 INT,
14     Col2 INT
15 )
16 GO
17 
18 -- Create a unique Clustered Index on both tables
19 CREATE UNIQUE CLUSTERED INDEX idx_ci ON t1(col1)
20 CREATE UNIQUE CLUSTERED INDEX idx_ci ON t2(col1)
21 GO
22 
23 -- Insert some records into both tables
24 INSERT INTO t1 VALUES (1, 1, 1), (2, 2, 2), (NULL, 3, 3)
25 INSERT INTO t2 VALUES (2, 2), (NULL, 3)
26 GO
27 GO

從T-SQL代碼里你可以看到,我也在2個表上創建了唯一聚集索引,並插入了一些測試記錄。現在讓我們來彼此交叉這2個表:

1 SELECT Col1, Col2 FROM t1
2 INTERSECT
3 SELECT Col1, Col2 FROM t2
4 GO

SQL Server返回2條記錄:列值為2和列值為NULL的記錄。這是和INNER JOIN的第1個大區別:如果NULL值出現在2個表里,這些記錄會被忽略。當你在Col列上進行2個表之間的INNER JOIN操作,含NULL值的記錄不會返回:

1 SELECT t1.col1, t1.col2 FROM t1
2 INNER JOIN t2 ON t2.col1 = t1.col1
3 GO

下圖顯示了INTERSECTINNER JOIN方法結果集的不同:

 

現在我們來分析下INTERSECT設置操作的執行計划。因為在Col列上你有支持的索引,查詢優化器可以翻譯INTERSECT操作為傳統的INNER JOIN邏輯操作。

 

但這里Nested Loop(Inner Join)並不真正進行INNER JOIN操作。我們來看下為什么。當你查看Nested Loop運算符屬性時,你會看到在Clustered Index Seek (Clustered)運算符上有剩余謂語(residual predicate)。

 

剩余謂語在Col2上評估,因為那列不是剛才創建的聚集索引導航結構的一部分。如我剛開始說的,SQL Server需要在2個表所有列找到匹配的行。使用Clustered Index Seek (Clustered)運算符和剩余謂語,SQL Server只檢查在t1表里是否有同樣列值的匹配記錄。而且Nested Loop運算符本身只返回從一個表的列值——這里是t1表。

 

因此INNER JOIN只是個左半連接(Left Semi Join):SQL Server檢查在右表里是否有我們匹配的記錄——如果是的話,匹配的記錄從左表返回。Clustered Index Seek (Clustered)上的剩余謂語可以通過提供在導航結構里包含所有必須的列來剔除,如下所示:

1 -- Create a supporting Non-Clustered Index
2 CREATE NONCLUSTERED index id_nci ON t1(Col1, Col2)
3 GO

現在當你再次看INTERSECT運算符的執行計划,你會看到SQL Server在剛才創建的索引進行Index Seek (NonClustered)操作,剩余謂語已經不再需要。

 

現在當我們刪除所有支持的索引結構,我們來看執行計划會變成什么樣。

1 -- Drop all supporting indexes
2 DROP INDEX id_nci ON t1
3 DROP INDEX idx_ci ON t1
4 DROP INDEX idx_ci ON t2
5 GO

當你再次對2個表進行INTERSECT,現在在執行計划里你會看到Nested Loop (Left Semi Join)運算符。SQL Server現在需要在執行計划里進行左半物理連接,通過在內部上進行Table Scan運算符和在Nested Loop里用剩余謂語進行逐行比較。

 

這個執行計划並不真的高效,因為在內部Table Scan需要反復進行——對來自外表返回的每一行。如果我們想盡可能高效的進行INTERSECT設置操作,支持的索引非常重要。 

小結

INTERSECT設置操作並不可怕,但幾乎沒人很懂它。當你用它時,你要意識到它和INNER JOIN.之間的區別。你也看到,有很好的索引設計對它非常重要,這樣的話查詢優化器可以生成很好的執行計划。

感謝關注!

參考文章:

https://www.sqlpassion.at/archive/2015/02/09/intersect-sql-server/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM