SQL Server中INNER JOIN與子查詢IN的性能測試


這個月碰到幾個人問我關於“SQL SERVER中INNER JOIN 與 IN兩種寫法的性能孰優孰劣?”這個問題。其實這個概括起來就是SQL Server中INNER JOIN與子查詢孰優孰劣(IN是子查詢的實現方式之一,本篇還是只對比INNER JOIN與子查詢IN的性能,如果展開INNER JOIN與子查詢性能對比,范圍太大了,沒法一一詳述)。下面這篇文章,我們就INNER JOIN與子查詢IN這兩種寫法孰優孰劣,在不同場景下進行一下測試對比一下,希望能解答你心中的疑惑。

 

下面例子以AdventureWorks2014為測試場景,測試表為Sales.SalesOrderHeader與Sales.SalesOrderDetail。 如下所示:

 
DBCC FREEPROCCACHE;
GO
DBCC DROPCLEANBUFFERS;
GO
 
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
 
SELECT  h.* FROM 
Sales.SalesOrderHeader h
WHERE SalesOrderID IN ( SELECT SalesOrderID FROM Sales.SalesOrderDetail)

clip_image001

 

clip_image002

 

DBCC FREEPROCCACHE;
GO
DBCC DROPCLEANBUFFERS;
GO
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
 
SELECT h.* FROM Sales.SalesOrderHeader h
INNER JOIN Sales.SalesOrderDetail d ON h.SalesOrderID = d.SalesOrderID

 

如下所示,兩種寫法的SQL的實際執行計划是幾乎一致。而且對比IO開銷也是一致。cpu time 與elapsed time 有所差別,這個是因為兩者返回的數據有所差別的緣故(SQL 1 返回 31465行數據, SQL 2返回 121317行數據),兩者在邏輯上實際上是不一致的。因為重復數據的緣故。撇開這個不談,光從性能上來考察兩種,它們幾乎是一模一樣。沒有優劣之分。

 

clip_image003

 

clip_image004

 

如果有人對上面的重復數據不明白的話,下面做個簡單的例子演示給大家看看。如下所示,截圖中INNER JOIN就會有重復數據。

 

CREATE TABLE P
(
    PID    INT ,
    Pname  VARCHAR(24)
)
 
INSERT INTO dbo.P
SELECT 1, 'P1' UNION ALL
SELECT 2, 'P2' UNION ALL
SELECT 3, 'P3'
 
 
CREATE TABLE dbo.C
(
    CID       INT ,
    PID       INT ,
    Cname  VARCHAR(24)
)
 
INSERT INTO dbo.c
SELECT 1, 1, 'C1' UNION ALL
SELECT 2, 1, 'C2' UNION ALL
SELECT 3, 2, 'C3' UNION ALL
SELECT 3, 3, 'C4'


clip_image005

 

其實下面SQL在邏輯上才是相等的,它們的實際執行計划與IO是一樣的。沒有優劣之分。

 

SELECT  h.* FROM 
Sales.SalesOrderHeader h
WHERE SalesOrderID IN ( SELECT SalesOrderID FROM Sales.SalesOrderDetail);
 
 
SELECT DISTINCT h.* FROM Sales.SalesOrderHeader h
INNER JOIN Sales.SalesOrderDetail d ON h.SalesOrderID = d.SalesOrderID;

 

clip_image006

 

那么我們再來看另外一個例子,測試一下兩者的性能差別。如下所示

 

SET STATISTICS IO ON;
SET STATISTICS TIME ON;
 
SELECT  C.*
FROM    Sales.Customer C
        INNER JOIN Person.Person P ON C.PersonID = P.BusinessEntityID;
 
 
SELECT  C.*
FROM    Sales.Customer C
WHERE  C.PersonID IN ( SELECT Person.Person.BusinessEntityID
                                     FROM   Person.Person );

 

 

INNER JOIN與子查詢IN的實際執行計划對比的百分比為66% VS 34% , 子查詢IN的性能還比 INNER JOIN的性能要好一些. IO幾乎無差別,cpu time 與elapsed time的對比情況來看,子查詢IN的性能確實要好一些。

 

這個是因為子查詢IN在這個上下文環境中,它使用右半連接(Right Semi Join)方式的Hash Match,即一個表中返回的行與另一個表中數據行進行不完全聯接查詢(查找到匹配的數據行就返回,不再繼續查找)。那么可以肯定的是,在這個場景(上下文)中,子查詢IN這種方式的SQL的性能比INNER JOIN 這種寫法的SQL要好。

 

clip_image007

clip_image008

 

 

那么我們再來看一個INNER JOIN性能比子查詢(IN)要好的案例。如下所示,我們先構造測試數據。

 

CREATE TABLE P
(
    P_ID    INT IDENTITY(1,1),
    OTHERCOL        CHAR(500),
    CONSTRAINT PK_P PRIMARY KEY(P_ID)
)
GO
 
BEGIN TRAN
DECLARE @I INT = 1
WHILE @I<=10000
BEGIN
    INSERT INTO P VALUES (NEWID())
    SET @I = @I+1
    IF (@I%500)=0
    BEGIN
        IF @@TRANCOUNT>0
        BEGIN
            COMMIT
            BEGIN TRAN
        END
    END
END
IF @@TRANCOUNT>0
BEGIN
    COMMIT
END
GO
 
 
CREATE TABLE C 
(
    C_ID  INT IDENTITY(1,1) ,
    P_ID   INT  FOREIGN KEY REFERENCES P(P_ID),
    COLN  CHAR(500),
    CONSTRAINT PK_C  PRIMARY KEY (C_ID) 
)
 
 
 
 
SET NOCOUNT ON;
 
DECLARE @I INT = 1
WHILE @I<=1000000
BEGIN
    INSERT INTO C VALUES ( CAST(RAND()*10 AS INT)+1,  NEWID())
    SET @I = @I+1
END
GO

 

構造完測試數據后,我們對比下兩者的性能差異

 

SET STATISTICS IO ON;
SET STATISTICS TIME ON;
 
SELECT C.* FROM dbo.C C
INNER JOIN dbo.P  P ON C.P_ID = P.P_ID
WHERE P.P_ID=8
 
 
SELECT * FROM dbo.C
WHERE P_ID IN (SELECT P_ID FROM dbo.P WHERE P_ID=8)

 

clip_image009

clip_image010

 

增加對應的索引后,這個性能差距更更明顯。 如下截圖所示

 

 
USE [AdventureWorks2014]
GO
CREATE NONCLUSTERED INDEX [IX_C_N1]
ON [dbo].[C] ([P_ID])
INCLUDE ([C_ID],[COLN])
GO

clip_image011

 

在生產環境遇到一個案例, 兩個視圖使用INNER JOIN 與 IN 兩種寫法,在性能上差距很大。 使用子查詢IN的性能比使用INNER JOIN的性能要好很多。如下截圖所示。因為視圖里面涉及多表。這樣肯定導致執行計划非常復雜,導致SQL用INNER JOIN 的寫法在性能上沒有用子查詢IN的寫法要快

 

clip_image012

 

其實一部分情況下,INNER JOIN 與 子查詢IN都是等價的。因為SQL Server優化器已經足夠聰明,能夠進行一些內部轉換,生成等價的計划。但是在某一些特殊場景下,各有優劣。不能武斷的就說INNER JOIN在性能上要比子查詢IN要好。一定要結合上下文環境具體來談性能優劣。否則沒有多大意義。另外,子查詢可以分為相關子查詢和無關子查詢,對於無關子查詢來說,Not In子句比較常見,但Not In潛在會帶來兩種問題,結果不正確和性能問題,具體可以參考在SQL Server中為什么不建議使用Not In子查詢

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM