我認爲面試官在表現差異方面是錯誤的。因爲連接的列在兩個表中都是唯一的且不爲空,所以NOT IN
,NOT EXISTS
和LEFT JOIN...WHERE IS NULL
查詢在語義上是相同的。 SQL
是一種聲明性語言,因此SQL Server優化程序可以提供最佳且相同的計劃,而不管現在查詢是否被表達。也就是說,它並不總是完美的,所以可能會有差異,特別是對於更復雜的查詢。
下面是一個腳本,演示了這一點。在我的SQL Server 2014框中,我看到前2個查詢(排序聚集索引掃描和合並連接)的相同執行計劃以及最後一個過濾運算符的添加。我預計所有3個人的表現都一樣,所以從性能的角度來看並不重要。我通常會使用NOT EXISTS
,因爲意圖更清晰,並且它避免了NOT IN子查詢返回NULL
的情況,因此導致由於UNKNOWN謂詞結果返回零行。
我不會推廣這樣的性能比較。如果連接的列允許NULL
或不能保證是唯一的,則這些查詢在語義上不是相同的,因此可能會產生不同的執行計劃。
CREATE TABLE dbo.employee (
empid int CONSTRAINT pk_employee PRIMARY KEY
, name varchar(20)
);
CREATE TABLE dbo.bademployee (
badempid int CONSTRAINT pk_bademployee PRIMARY KEY
, name varchar(20)
);
WITH
t4 AS (SELECT n FROM (VALUES(0),(0),(0),(0)) t(n))
,t256 AS (SELECT 0 AS n FROM t4 AS a CROSS JOIN t4 AS b CROSS JOIN t4 AS c CROSS JOIN t4 AS d)
,t16M AS (SELECT ROW_NUMBER() OVER (ORDER BY (a.n)) AS num FROM t256 AS a CROSS JOIN t256 AS b CROSS JOIN t256 AS c)
INSERT INTO dbo.employee(empid, name)
SELECT num, 'Employee name ' + CAST(num AS varchar(10))
FROM t16M
WHERE num <= 10000;
INSERT INTO dbo.bademployee(badempid, name)
SELECT TOP 5 PERCENT empid, name
FROM dbo.employee
ORDER BY NEWID();
GO
UPDATE STATISTICS dbo.employee WITH FULLSCAN;
UPDATE STATISTICS dbo.bademployee WITH FULLSCAN;
GO
SELECT *
FROM employee
WHERE empid NOT IN (SELECT badempid from bademployee);
SELECT *
FROM Employee e
WHERE NOT EXISTS (
SELECT 1
FROM bademployee b
WHERE e.empid = b.badempid);
SELECT e.*
FROM Employee e
LEFT JOIN bademployee b ON e.empid = b.badempid
WHERE b.badempid IS NULL;
GO
我喜歡第二個。但這是IT絕對新鮮的正常問題。? – 2014-09-20 06:49:43
@PATILDADA第二個查詢性能最差。除非在右側查詢中有NULL值,否則NOT IN不一定是錯誤的。不存在是優選的。檢查他發佈的鏈接,這個主題非常豐富。 – 2014-09-20 08:33:40
@PADILDADA,您發佈的鏈接中的示例與面試問題不同。在Pinal Day的例子中,連接的列不是兩個表的主鍵。重要的是,Pinal Dave說「這取決於」,這幾乎總是這些問題的正確答案。 – 2014-09-20 16:40:07