[英]How to improve performance of this SQL Server query?
在Web開發人員面試中有人問我這個問題。 在我的回答后,面試官在第二張桌子說:
我有兩個表employee
和bademployee
:
employee (
empid int pk,名稱varchar(20)`) bademployee
( badempid int pk, name varchar(20)
) 現在,我只選擇優秀員工。
我的答案是:
SELECT *
FROM employee
WHERE empid NOT IN (SELECT badempid from bademployee)
他說此查詢不利於性能。
任何人都可以告訴我如何通過不使用否定項(不在in!!=中)來編寫針對相同結果的查詢。
可以使用LEFT OUTER JOIN
嗎?
可以使用帶有NULL
檢查的OUTER JOIN
或使用NOT EXISTS
來重寫它。 我更喜歡NOT EXISTS
:
SELECT *
FROM Employee e
WHERE NOT EXISTS (
SELECT 1
FROM bademployee b
WHERE e.empid = b.badempid)
這是OUTER JOIN
,但是我相信使用NOT EXISTS
會更好地表現。
SELECT e.*
FROM Employee e
LEFT JOIN bademployee b ON e.empid = b.badempid
WHERE b.badempid IS NULL
這是一篇有關性能差異的有趣文章: http : //sqlperformance.com/2012/12/t-sql-queries/left-anti-semi-join
無論別人怎么說,您都需要檢查執行計划,並根據所得出的結論得出結論。 永遠不要只相信別人,就可以研究他的主張,並通過有關該主題的文檔進行核實,在這種情況下,還可以使用執行計划清楚地告訴您正在發生的事情。
SQL Authority博客中的一個示例表明,LEFT JOIN解決方案的性能比NOT IN解決方案差很多。 這是由於查詢計划者完成了LEFT ANTI SEMI JOIN,通常比LEFT JOIN + NULL檢查要好得多。 行數很少時可能會有例外。 之后,作者還告訴您與我在第一段中所做的相同:始終檢查執行計划。
SQL Performance博客的另一篇博客文章進一步介紹了實際的性能測試結果。
TL; DR:就性能而言,NOT EXISTS和NOT IN處於同一級別,但是由於NULL值的問題,因此不建議使用NOT EXISTS。 另外,不要僅僅相信任何人的主張,研究和驗證您的執行計划。
我認為面試官關於績效差異是錯誤的。 因為聯接的列在兩個表中都是唯一的並且不為null,所以NOT IN
, NOT EXISTS
和LEFT JOIN...WHERE IS NULL
查詢在語義上是相同的。 SQL
是一種聲明性語言,因此,無論現在是否表達查詢,SQL Server優化器都可以提供最佳且相同的計划。 也就是說,它並不總是完美的,因此可能會有差異,尤其是對於更復雜的查詢。
以下是演示此腳本。 在我的SQL Server 2014框中,我看到前兩個查詢的執行計划相同(有序的聚集索引掃描和合並聯接),最后一個查詢添加了過濾器運算符。 我希望這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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.