Answers:
最大的区别不是联接与不存在,而是(按书面说明)SELECT *
。
在第一个示例中,您同时 从A
和中获取了所有列B
,而在第二个示例中,您仅从中获取了列A
。
在SQL Server中,在一个非常简单的示例中,第二种变体要快一些:
创建两个示例表:
CREATE TABLE dbo.A
(
A_ID INT NOT NULL
PRIMARY KEY CLUSTERED
IDENTITY(1,1)
);
CREATE TABLE dbo.B
(
B_ID INT NOT NULL
PRIMARY KEY CLUSTERED
IDENTITY(1,1)
);
GO
在每个表中插入10,000行:
INSERT INTO dbo.A DEFAULT VALUES;
GO 10000
INSERT INTO dbo.B DEFAULT VALUES;
GO 10000
从第二个表中删除每第5行:
DELETE
FROM dbo.B
WHERE B_ID % 5 = 1;
SELECT COUNT(*) -- shows 10,000
FROM dbo.A;
SELECT COUNT(*) -- shows 8,000
FROM dbo.B;
执行两个测试SELECT
语句变体:
SELECT *
FROM dbo.A
LEFT JOIN dbo.B ON A.A_ID = B.B_ID
WHERE B.B_ID IS NULL;
SELECT *
FROM dbo.A
WHERE NOT EXISTS (SELECT 1
FROM dbo.B
WHERE b.B_ID = a.A_ID);
执行计划:
第二个变体不需要执行过滤操作,因为它可以使用左反半连接运算符。
从逻辑上讲,它们是相同的,但是NOT EXISTS
更接近于您所要求的AntiSemiJoin,通常是首选。它还突出显示了您无法访问B中的列,因为它仅用作过滤器(而不是使它们具有NULL值)。
许多年前(SQL Server 6.0 ish)LEFT JOIN
速度更快,但很长一段时间以来并非如此。这些天,NOT EXISTS
速度略快。
Access中最大的影响是该JOIN
方法必须在过滤之前完成连接,然后在内存中构造连接集。使用NOT EXISTS
它检查行,但不为列分配空间。另外,一旦找到行,它就会停止寻找。在Access中,性能会有较大差异,但通常的经验法则是NOT EXISTS
速度会稍快一些。由于涉及更多因素,我不太愿意说这是“最佳实践”。
我注意到的一个例外是使用Linked Servers时NOT EXISTS
具有优越性(但略有优势)。LEFT JOIN ... WHERE IS NULL
通过检查执行计划,可以发现NOT EXISTS
操作员以嵌套循环的方式执行。从而在每行的基础上执行(我认为这很有意义)。
INSERT INTO #t (a,b,c) SELECT a,b,c FROM LinkedServer.database.dbo.table WHERE x=y
的NOT EXISTS (...)
子句然后针对数据库的该临时副本运行子句,从而通过链接的服务器链接复制远程数据。
WHERE A.idx NOT IN (...)
是不相同的,由于三价的行为NULL
(即NULL
不等于NULL
(也不不等),因此,如果您有任何NULL
的tableB
你会得到意想不到的结果!)