簡體   English   中英

如何提高此SQL Server查詢的性能?

[英]How to improve performance of this SQL Server query?

在Web開發人員面試中有人問我這個問題。 在我的回答后,面試官在第二張桌子說:

我有兩個表employeebademployee

  • employee ( empid int pk,名稱varchar(20)`)
  • bademployeebadempid 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 INNOT EXISTSLEFT 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM