EXCEPT运算符背后的算法是什么?


10

在SQL Server的幕后,Except运算符如何工作的内部算法是什么?是否在内部对每行进行哈希处理并进行比较?

David Lozinksi进行了一项研究,即SQL:在尚不存在的新记录中插入新记录的最快方法。与下面的结果紧密相关。

假设:我认为Left join最快,因为它只比较1列,而Except花费的时间最长,因为它必须比较All列。
有了这些结果,现在我们的思维是“除了自动且内部对每一行进行哈希处理?”。我看了看执行计划,它确实利用了一些哈希。

背景:我们的团队正在比较两个堆表。表A将不在表B中的行插入到表B中。

堆表(来自旧文本文件系统)没有主键/向导/标识符。一些表具有重复的行,因此我们找到了每一行的哈希,并删除了重复项,并创建了主键标识符。

1)首先,我们运行一个except语句,排除(hash列)

select * from TableA
Except
Select * from TableB,

2)然后我们在HashRowId的两个表之间进行左联接比较

select * 
FROM dbo.TableA A
left join dbo.TableB B
    on A.RowHash =  B.RowHash
where B.Hash is null

令人惊讶的是,例外声明插入是最快的。

结果实际上与David Lozinksi的测试结果很接近

在此处输入图片说明


Answers:


10

在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可以导致特殊的内部哈希比较。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.