1. 程式人生 > >多表連線的三種方式詳解 HASH JOIN MERGE JOIN NESTED LOOP【3】

多表連線的三種方式詳解 HASH JOIN MERGE JOIN NESTED LOOP【3】

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

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

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

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

        · 取決於表大小

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

        · 取決於連線列是否排序

測試環境:

        記憶體:4GB

        資料庫伺服器:SQLServer 2008 (RTM)

  1. createtable tableA (id int identity ,namevarchar(50))  
  2. declare @i int
  3. set @i=0  
  4. while (@i<100)  
  5. begin
  6. insertinto tableA (name)  
  7. selectnamefrom master.dbo.spt_values  
  8. set
     @[email protected]+1  
  9. end
  10. --select COUNT(*) from dbo.tableA --250600
  11. go  
  12. createtable tableB (id int identity ,namevarchar(50))  
  13. declare @i int
  14. set @i=0  
  15. while (@i<100)  
  16. begin
  17. insertinto tableB (name)  
  18. selectnamefrom master.dbo.spt_values  
  19. set @[email protected]+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. createunique clustered index cx_tableA on tableA (id)  
  2. createunique clustered index cx_tableB on tableB (id)  

測試1:大表,有索引


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

測試2:中表,沒有索引

首先建立表:

  1. createtable tableC (id int identity,namevarchar(50))  
  2. insertinto tableC (name)  
  3. selectnamefrom master.dbo.spt_values  
  4. -- select COUNT(*) from dbo.tableC --2506
  5. createtable tableD (id int identity,namevarchar(50))  
  6. insertinto tableD (name)  
  7. selectnamefrom 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. createunique clustered index cx_tableC on tableC (id)  
  2. createunique clustered index cx_tableD on tableD (id)  


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

測試3:小表,沒有索引
  1. createtable tableE (id int identity,namevarchar(50))  
  2. insertinto tableE (name)  
  3. selecttop 10 namefrom master.dbo.spt_values  
  4. -- select COUNT(*) from dbo.tableE --10
  5. createtable tableF (id int identity,namevarchar(50))  
  6. insertinto tableF (name)  
  7. selecttop 10 namefrom master.dbo.spt_values  
  8. -- select COUNT(*) from dbo.tableF --10


測試3:小表,有索引

建立聚族索引:

  1. createunique clustered index cx_tableE on tableE (id)  
  2. createunique 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. select * from dbo.tableA A join tableE E  
  4. on (a.id=E.id)  
  5. select * from dbo.tableC C join tableE E  
  6. 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. select * from dbo.tableA A inner merge join tableB B  
  4. 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是為那些有索引的小表或其中人一個有索引的大表。它對那些小表連線,需要迴圈執行從一個到另一個表的按行比較的情況下工作最好的。

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

相關推薦

連線方式 hash joinmerge joinnested loop

在多表聯合查詢的時候,如果我們檢視它的執行計劃,就會發現裡面有多表之間的連線方式。多表之間的連線有三種方式:Nested Loops,Hash Join 和 Sort Merge Join.具體適用哪種型別的連線取決於 當前的優化器模式 (ALL_ROWS 和 RULE) 取決於表大小 取決於連線列

連線方式 HASH JOIN MERGE JOIN NESTED LOOP

在多表聯合查詢的時候,如果我們檢視它的執行計劃,就會發現裡面有多表之間的連線方式。 之前打算在sqlplus中用執行計劃的,但是格式看起來有點亂,就用Toad 做了3個截圖。  

連線方式 HASH JOIN MERGE JOIN NESTED LOOP3

SQL server 內部實現了三種類型的內連線運算,大多數人從來沒有聽說過這些連線型別,因為它們不是邏輯連線也很少被用於程式碼中。那麼它們什麼時候會被用到呢?答案是要依情況而定。這就意味著要依賴於記錄集和索引。查詢優化器總是智慧的選擇最優的物理連線型別。我們知道SQL優

REACT NATIVE 系列教程之四重新整理元件RENDER(重新渲染)的方式

開發過遊戲的都應該很清楚,“刷屏”是多麼的重要。其實開發應用也如此,當元件的資料被修改後,如何及時更新元件呈現出最新的資料與效果一樣至關重要。那麼這裡Himi大概講三種常用的方式:this.setState()  【最為常用】這是在事件處理函式中和請求回撥函式中觸發 UI 更新的主要方法。一般情況下setSt

spring建立物件的方式

前言 我們知道spring框架的核心就是IOC容器了,那麼IOC容器主要的作用就是建立物件和處理物件之間的依賴關係。本文主要講解IOC容器建立物件的三種方式。 建立物件的三種方式 1) 呼叫無引數構造器 2) 帶引數構造器 3) 工廠建立物件

css3中建立動畫的方式

$div1.transition({ transform : "translateX(300px)" },3000,"linear", function(){ $div2.transition({ transform : "translateX(300px)" },30

mysql備份的方式

mysql> FLUSH TABLES WITH READ LOCK; 請求讀鎖 注:不要退出,另起一個終端: mysql> SHOW MASTER STATUS;          檢視二進位制檔案的位置 +------------------+----------+----------

javascript函式定義方式

定義函式的三種方式 function語句式 function test1() { console.info("test1"); } 函式的直接變數 ECMAScript var test2 = function () { cons

往HIVE中匯入匯出資料的幾方式

一:往HIVE表中匯入匯出資料語法結構:[ ]帶括號的表示可選擇欄位LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTOTABLE tablename

用 Python 連線 MySQL 的幾方式

儘管很多 NoSQL 資料庫近幾年大放異彩,但是像 MySQL 這樣的關係型資料庫依然是網際網路的主流資料庫之一,每個學 Python 的都有必要學好一門資料庫,不管你是做資料分析,還是網路爬蟲,Web 開發、亦或是機器學習,你都離不開要和資料庫打交道,而 MySQL 又是最

JS創建對象的幾方式

演員 sta say object ron 操作 tar obj 構造 Js是一門面向對象的語言,裏面沒有類的思想,所以直接是創建對象,下面介紹幾種創建對象的方法: 1.對象字面量的方法:記住鍵值對格式:{key:value,key :value} 實例: Var

new和delete的形式

分別是 額外 ID 調用 pre else class code alloc 一、new操作符、delete操作符 class String { public: String(const char *str="") { if

排列硬幣 arrange coins 解法

* 排列硬幣 * https://leetcode-cn.com/problems/arranging-coins/description/ * 你總共有 n 枚硬幣,你需要將它們擺成一個階梯形狀,第

Mysql 刪除資料方式

刪除程度可從強到弱如下排列: 1.  drop table tb;     drop 是直接將表格刪除,無法找回。例如刪除 user 表: drop table user; 2.  truncate (table) tb;     truncate 是刪除表中所有資

jQuery使用JSONP實現跨域獲取資料的方法

本文例項講述了jQuery使用JSONP實現跨域獲取資料的三種方法。分享給大家供大家參考,具體如下: 第一種方法是在ajax函式中設定dataType為'jsonp' $.ajax({ dataType: 'jsonp', url: 'http://www.a

PHP實現鏈式操作的方法

ret 思想 ont 過濾字符 一個 詳解 rgs 通過 span 這篇文章主要介紹了PHP實現鏈式操作的三種方法,結合實例形式分析了php鏈式操作的相關實現技巧與使用註意事項,需要的朋友可以參考下 本文實例講述了PHP實現鏈式操作的三種方法。分享給大家供大家參考,具

Spring boot實現熱部署的兩方式

熱部署是什麼大家都知道在專案開發過程中,常常會改動頁面資料或者修改資料結構,為了顯示改動效果,往往需要重啟應用檢視改變效果,其實就是重新編譯生成了新的 Class 檔案,這個檔案裡記錄著和程式碼等對應的各種資訊,然後 Class 檔案將被虛擬機器的 ClassLoader 載入。而熱部署正是利用了這個特點,它

SpringMVC的controller向jsp傳遞資料的五方式

第一種   使用model來儲存資料到前臺我的專案目錄為我的controller頁面程式碼@RequestMapping("/demo") public String Model(Model model

web頁面跳轉幾方式

在做web開發中,頁面跳轉的方式有很多種,然而有些時候這些跳轉如何用到恰到好處卻很容易被忽視。 客戶端觸發跳轉有如下幾種 使用meta元資訊 ? 1 2 <!--如下表示

SpringMVC4.3x教程之七國際化的實現

國際化(internationalization)是設計和製造容易適應不同區域要求的產品的一種方式。它要求從產品中抽離所有地域語言,國家/地區和文化相關的元素。換言之,應用程式的功能和程式碼設計考慮在不同地區執行的需要,其程式碼簡化了不同本地版本的生產。開發這樣