Merge join、Hash join、Nested loop join對比分析


SQL server 內部實現了三種類型的內連接運算,大多數人從來沒有聽說過這些連接類型,因為它們不是邏輯連接也很少被用於代碼中。那么它們什么時候會被用到呢?答案是要依情況而定。這就意味着要依賴於記錄集和索引。查詢優化器總是智能的選擇最優的物理連接類型。我們知道SQL優化器創建一個計划開銷是基於查詢開銷的,並依據此來選擇最佳連接類型。

        那查詢優化器究竟是怎樣從內部選擇連接類型的呢?

        SQLServer在內部為查詢優化器對連接類型的選擇實現了一些算法,讓我們來看下面的一些練習示例,最后來做總結。

        首先我給出一些基本的思想,連接是怎樣工作什么時候工作,優化器又是怎樣決定使用哪種類型的內連接。

        · 取決於表大小

        · 取決於連接列是否有索引

        · 取決於連接列是否排序

測試環境:

        內存:4GB

        數據庫服務器:SQLServer 2008 (RTM)

  1. create table tableA (id int identity ,name varchar(50))
  2. declare @i int
  3. set @i=0
  4. while (@i<100)
  5. begin
  6. insert into tableA (name)
  7. select name from master.dbo.spt_values
  8. set @i=@i+1
  9. end
  10. --select COUNT(*) from dbo.tableA --250600
  11. go
  12. create table tableB (id int identity ,name varchar(50))
  13. declare @i int
  14. set @i=0
  15. while (@i<100)
  16. begin
  17. insert into tableB (name)
  18. select name from master.dbo.spt_values
  19. set @i=@i+1
  20. end
  21. -- select COUNT(*) from dbo.tableB --250600
  22. select * from dbo.tableA A join tableB B
  23. on (a.id=b.id)

 

測試1:大表,沒有索引

現在來創建一個聚族索引:

  1. create unique clustered index cx_tableA on tableA (id)
  2. create unique clustered index cx_tableB on tableB (id)

 

測試1:大表,有索引

如果連接中的任何一個表有索引那么將采用Hash Join。我並沒有貼上所有結果截圖,如果你感興趣你可以刪除任何一個表中的索引來做測試。

測試2:中表,沒有索引

首先創建表:

  1. create table tableC (id int identity,name varchar(50))
  2. insert into tableC (name)
  3. select name from master.dbo.spt_values
  4. -- select COUNT(*) from dbo.tableC --2506
  5. create table tableD (id int identity,name varchar(50))
  6. insert into tableD (name)
  7. select name from master.dbo.spt_values
  8. select * from dbo.tableC C join tableD D
  9. on (C.id=D.id)
  10. -- select COUNT(*) from dbo.tableD --2506


測試2:中表,有索引

首先還是創建一個聚族索引:

  1. create unique clustered index cx_tableC on tableC (id)
  2. create unique clustered index cx_tableD on tableD (id)


對於中等大小的表,如果連接中的任何一個表有索引,那么將采用Merge Join。

測試3:小表,沒有索引

  1. create table tableE (id int identity,name varchar(50))
  2. insert into tableE (name)
  3. select top 10 name from master.dbo.spt_values
  4. -- select COUNT(*) from dbo.tableE --10
  5. create table tableF (id int identity,name varchar(50))
  6. insert into tableF (name)
  7. select top 10 name from master.dbo.spt_values
  8. -- select COUNT(*) from dbo.tableF --10


測試3:小表,有索引

創建聚族索引:

  1. create unique clustered index cx_tableE on tableE (id)
  2. create unique clustered index cx_tableF on tableF (id)


對於小表,如果任何一個表中有索引,那么將采用Nested Loop Join。

同樣也可以從另一個方向來做比較,比如大表對比中表對比小表。

  1. select * from dbo.tableA A join tableC C
  2. on (a.id=C.id)
  3.  
  4. select * from dbo.tableA A join tableE E
  5. on (a.id=E.id)
  6.  
  7. select * from dbo.tableC C join tableE E
  8. on (C.id=E.id)

 

在這種情況下若所有或部分表都有索引則采用Nested Loop Join,如果都沒有則使用HashJoin。

當然你也可以強制優化器使用任何一種連接類型,但這並不是一種值得推薦的做法。查詢優化器很智能,能夠動態的選擇最優的一個。這里我只是顯示調用了MergeJoin,所以優化器使用MergeJoin替代本來應使用HashJoin (測試1沒有索引)。

  1. select * from dbo.tableA A join tableB B
  2. on (A.id=B.id)option (merge join)
  3.  
  4. select * from dbo.tableA A inner merge join tableB B
  5. on (A.id=B.id)


表1  測試唯一聚族索引

根據上表:

Ø 如果兩個表都沒有索引則查詢優化器內部會選擇Hash Join

Ø 如果兩個表都有索引則內部會選擇Merge Join(大表)/NestedLoop Join(小表)

Ø 如果其中的一個表有索引則查詢優化器內部會選擇Merge Join(中表)/HashJoin(大表)/NestedLoop Join(小表&大表 vs 小表)

表2  測試聚族索引(createclustered indexcx_tableA ontableA (id))

 

Table size

With index (Both)

Without Index(Both)

Either of table has index

 

Big (Both)

HASH

HASH

HASH

 

Medium (Both)

HASH

HASH

HASH

 

Small (Both)

NESTED LOOP

NESTED LOOP

HASH

 

Big Vs Small(medium)

HASH

HASH

HASH

 

 

根據上表:

       這個測試是在沒有使用唯一聚族索引下完成,可以知道如果創建索引的時候沒有使用UNIQUE關鍵字則無法保證SQLServer會知道這是UNIQUE數據,所以它默認會創建4字節整數來作為唯一標識符。

       根據上表如果創建聚族索引沒有使用Unique關鍵字則不會使用MergeJoin。

       謝謝@Dave的郵件,現在加上第二個圖表了。

總結:

Merge Join

Merge Join是為那些在連接列上有索引的表,索引可以是聚族索引或者非聚族索引。Merge是這種情況最好的Join類型,需要兩個表都有索引,所以它已經排好序並更容易匹配和返回數據。

Hash Join

Hash Join是為那些沒有索引或者其中任一個有索引的大表。對於這種情況它是最好的Join類型,為什么呢?因為它能夠很好的工作於沒有索引的大表和並行查詢的環境中,並提供最好的性能。大多數人都說它是Join的重型升降機。

Nested Loop Join

Nested Loop Join是為那些有索引的小表或其中人一個有索引的大表。它對那些小表連接,需要循環執行從一個到另一個表的按行比較的情況下工作最好的。

我希望你現在能理解查詢優化器是如何選擇最優的查詢類型。

原文地址:Merge join Vs Hash join Vs Nested loop join


免責聲明!

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



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