2014-09-20 110 views
1

我在網絡開發者面試時被問到這個問題。我的回答面試官說你以後在第二個表:(如何提高此SQL Server查詢的性能?

我有兩個表employeebademployee

  • employee ( EMPID INT PK,名稱VARCHAR(20)`)
  • bademployeebadempid int pk, name varchar(20)

現在,我只想選擇優秀的員工。

我的回答是:

SELECT * 
FROM employee 
WHERE empid NOT IN (SELECT badempid from bademployee) 

他說,這個查詢是不好的表現。

任何人都可以告訴我如何爲相同的結果編寫查詢,不使用否定詞(不在,!=)。

是否可以使用LEFT OUTER JOIN

回答

3

這可以使用OUTER JOINNULL檢查或使用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

+0

我喜歡第二個。但這是IT絕對新鮮的正常問題。? – 2014-09-20 06:49:43

+0

@PATILDADA第二個查詢性能最差。除非在右側查詢中有NULL值,否則NOT IN不一定是錯誤的。不存在是優選的。檢查他發佈的鏈接,這個主題非常豐富。 – 2014-09-20 08:33:40

+0

@PADILDADA,您發佈的鏈接中的示例與面試問題不同。在Pinal Day的例子中,連接的列不是兩個表的主鍵。重要的是,Pinal Dave說「這取決於」,這幾乎總是這些問題的正確答案。 – 2014-09-20 16:40:07

1

不管別人怎麼說,你需要檢查的執行計劃和底座您的是什麼賽斯結論。永遠不要相信別人聲稱這個或那個,研究他的主張並通過關於這個主題的文檔來驗證,在這種情況下,執行計劃清楚地告訴你發生了什麼。來自SQL Authority博客的

One example顯示LEFT JOIN解決方案的性能比NOT IN解決方案差得多。這是由查詢計劃程序執行的左側反半連接引起的,該查詢計劃程序通常比LEFT JOIN + NULL檢查執行得更好。行數很少時可能會有例外。作者之後還會跟我在第一段中所做的一樣告訴你:總是檢查執行計劃。

Another blog post來自SQL Performance博客進一步介紹了這個實際性能測試結果。

TL; DR:在性能方面NOT EXISTS和NOT IN在同一級別,但NOT EXISTS最好是由於NULL值的問題。此外,不要只相信任何人聲稱,研究和驗證您的執行計劃。

1

我認爲面試官在表現差異方面是錯誤的。因爲連接的列在兩個表中都是唯一的且不爲空,所以NOT IN,NOT EXISTSLEFT 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 
+0

謝謝你。翔實。 – 2014-09-25 18:54:12