在SQL Server的幕后,Except运算符如何工作的内部算法是什么?
我不会说有一种特殊的内部算法EXCEPT
。对于A EXCEPT B
,引擎从A中获取不同的(如果需要)元组,并减去在B中匹配的行。没有特殊的查询计划运算符。通过使用排序或联接会看到的典型运算符来实现distinct和subtract。支持嵌套循环联接,合并联接和哈希联接。为了说明这一点,我将1500万行放入一对堆中:
DROP TABLE IF EXISTS dbo.TABLE_1;
CREATE TABLE dbo.TABLE_1 (
COL1 BIGINT NULL,
COL2 BIGINT NULL
);
INSERT INTO dbo.TABLE_1 WITH (TABLOCK)
SELECT TOP (15000000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), NULL
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
OPTION (MAXDOP 1);
DROP TABLE IF EXISTS dbo.TABLE_2;
CREATE TABLE dbo.TABLE_2 (
COL1 BIGINT NULL,
COL2 BIGINT NULL
);
INSERT INTO dbo.TABLE_2 WITH (TABLOCK)
SELECT TOP (15000000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), NULL
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
OPTION (MAXDOP 1);
优化程序通常根据成本决定如何实现排序和联接。有两个堆,我得到了预期的哈希联接。通过添加索引或更改任一表中的数据,您自然可以看到其他联接类型。下面,我仅出于说明目的,强制使用提示进行合并和循环连接:
是否在内部对每行进行哈希处理并进行比较?
否。它与任何其他联接一样实现。一个区别是NULL被视为相等。这是一种特殊的比较类型,您可以在执行计划中看到:<Compare CompareOp="IS">
。但是,您可以使用不包含EXCEPT
关键字的T-SQL获得相同的计划。例如,以下与EXCEPT
使用哈希联接的查询具有完全相同的查询计划:
SELECT t1.*
FROM
(
SELECT DISTINCT COL1, COL2
FROM dbo.TABLE_1
) t1
WHERE NOT EXISTS (
SELECT 1
FROM dbo.TABLE_2 t2
WHERE (t1.COL1 = t2.COL1 OR (t1.COL1 IS NULL AND t2.COL1 IS NULL))
AND (t1.COL2 = t2.COL2 OR (t1.COL2 IS NULL AND t2.COL2 IS NULL))
);
对执行计划的XML进行比较,只会发现别名和类似内容之间的肤浅差异。哈希联接的探针残差进行行比较。这两个查询都相同:
如果您仍然有疑问,我会以最高的可用采样率运行PerfView,以获取带有EXCEPT
该查询的查询的调用堆栈和不带有该查询的查询的调用堆栈。这是并排的结果:
没有真正的区别。由于计划中的哈希匹配,因此存在引用哈希的调用堆栈。如果添加索引以获取自然的合并联接,则在调用堆栈中将看不到任何对哈希的引用:
发生的任何哈希归因于哈希匹配运算符的实现。没有什么特别的EXCEPT
可以导致特殊的内部哈希比较。