1. 程式人生 > >SQL連線操作符介紹(迴圈巢狀, 雜湊匹配和合並連線)

SQL連線操作符介紹(迴圈巢狀, 雜湊匹配和合並連線)

  今天我將介紹在SQLServer 中的三種連線操作符型別,分別是:迴圈巢狀、雜湊匹配和合並連線。主要對這三種連線的不同、複雜度用範例的形式一一介紹。

簡介:什麼是連線操作符

  連線操作符是一種演算法型別,它是SQLServer優化器為了實現兩個資料集合之間的邏輯連線選擇的操作符。優化器可以基於請求查詢、可用索引、統計資訊和估計行數等不同的場景為每一套資料選擇不同的演算法

  通過檢視執行計劃可以發現操作符如何被使用。接下來我們看一下如何具體使用。

NESTED LOOPS(迴圈巢狀)

  我們通過下面的例子來展示一下(查詢2001年7月份的資料):

SELECT
OH.OrderDate, OD.OrderQty, OD.ProductID, OD.UnitPrice
FROM
Sales.SalesOrderHeader AS OH
JOIN
Sales.SalesOrderDetail AS OD
ON
OH.SalesOrderID = OD.SalesOrderID
WHERE
OH.OrderDate BETWEEN '2001-07-01' AND '2001-07-31'

執行計劃的結果如下:

join types 1 exec plan

圖右上方的叫“outer input”,在其下面的叫做“inner input” 

本質上講,“Nested Loops”操作符就是:為每一個記錄的外部輸入找到內部輸入的匹配行。

技術上講,這意味著外表聚集索引被掃描獲取外部輸入相關的記錄,然後內表聚集索引查詢每一個匹配外表索引的記錄。

我們可以通過把滑鼠放在聚集索引掃描操作符上面來驗證這個資訊,請看這個tooltip:

join types 2 details

看這個執行的估計行數是1,索引查詢tooltip如下:

join types 3 details

這次發現執行的估計行數是179,這是很接近返回的外部輸入行的。

按照複雜度計算(假設N是外部輸出的行數,M是總行數在SalesOrderDetai表的):查詢複雜度是O(NlogM),這裡logM是在內部輸入表的每次查詢的複雜度。

當外部輸入比較小並且內部輸入有索引在連線的欄位上的時候SQLServer 優化器更喜歡選擇這種操作符型別(Nested Loop)。外部和內部輸入的資料行差距越大,這個操作符提供的效能越高。

MERGE Join(合併連線)

“Merge”演算法是連線兩個較大且按序儲存的在連線鍵上最有效的方式。請看一下下面這個查詢例子(查詢返回使用者和銷售表的ID):

SELECT
OC.CustomerID, OH.SalesOrderID
FROM
Sales.SalesOrderHeader AS OH
JOIN
Sales.Customer AS OC
ON
OH.CustomerID = OC.CustomerID

查詢執行計劃如下:

join types 4 exec plan

  • 首先我們注意到兩套資料是在CustomerID上是有序的:因為聚集索引是有序的且在SalesorderHeader表上該欄位是非聚集索引。
  • 根據在操作符的箭頭(滑鼠放在上面),我們能看到每個返回結果行數都是很大的。
  • 除此之外,在On 的子句後面要用=操作符。

就是這三個因素會導致優化器選擇Merge Join查詢操作符。 

使用這種連線操作符的最大的效能就是兩個輸入操作符執行一次。我們能把滑鼠放在兩個資料的上面看一下執行的次數都是1,也就是說演算法是很有效率的。

合併連線同時讀取兩個輸入然後比較他們。如果匹配就返回,否則,行數較小的被放棄,因為兩邊輸入是有序的。放棄的行不再匹配任何行。

知道其中一個表完畢一直重複匹配,即使另一個表還有資料,那麼最大的時間複雜的消耗就是兩個表完全不同鍵值,那麼最大的複雜度就是: O(N+M)。

HASH Match(雜湊匹配)

“Hash”連線是我們稱為 “the go-to guy” 的操作符。當其他連線操作符都不支援的場景時,就會選擇這種操作符。比如當表恰好不排序,或者沒有索引時。當優化器選擇這種操作符,一般來說可能是我們沒有做好一些基礎工作(例如,加索引)。但是有些情況(複雜查詢)沒有別的方式,只能選擇它。

請看下面這個查詢(獲取contacts 表中姓和名中以“John”開始的包含銷售的ID欄位的資料集):

SELECT
OC.FirstName, OC.LastName, OH.SalesOrderID
FROM
Sales.SalesOrderHeader AS OH
JOIN
Person.Contact AS OC
ON
OH.ContactID = OC.ContactID
WHERE
OC.FirstName LIKE 'John%'

The execution plan looks like this:

join types 5 exec plan

由於ContactID列沒有索引,所以選擇雜湊操作符。

在深入理解這個例子之前,介紹兩個重要的概念:一個是“Hashing”函式,一個是“Hash Table”。

函式是一個程式性函式,它接收1或者多個值然後轉換他們為一個符號值(通常是數字)。這個函式通常是單向的,意味著不能反轉回來原始值,但是確定性保證如果你提供了相同的值,符號值是完全一樣的。也就是說,幾個不同的輸入值,可以有相同的Hash值。

“Hash Table”是一個數據結構,把所有行都放到一個相同尺寸的桶裡面。每一個桶代表一個雜湊值。這意味著當你啟用函式的行,使用結果你就會知道它屬於哪個桶。

利用統計資訊,SQLServer 會選擇較小的兩個資料輸入來提供構造輸入,並且輸入被用來在記憶體中建立雜湊表。如果沒有足夠的記憶體,在tempdb中會使用物理磁碟。在雜湊表建立後,SQLServer將從較大的表中得到資料,叫做探測輸入。利用雜湊匹配函式與雜湊表比較,然後返回匹配行。在圖形執行計劃中,構造輸入的查詢在上面,探測輸入的查詢在下面。

只要較小的表非常小,這個演算法就是非常有效的。但是如果兩個表都非常大,這可能是非常低效的執行計劃。

查詢Hints

利用Hints,破事SQLServer使用指定的連線型別。但是強烈不推薦這麼做,尤其在生產環境,因為沒有永恆的最佳選擇(因為資料在變化),並且優化器通常是正確的。

新增OPTION 子句作為查詢的結尾,使用關鍵字LOOP JOINMERGE JOIN 或者 HASH JOIN可以強制執行連線。

看看如何實現:

SELECT OC.CustomerID, OH.SalesOrderID
FROM Sales.SalesOrderHeader AS OH
JOIN Sales.Customer AS OC
ON OH.CustomerID = OC.CustomerID
OPTION (HASH JOIN)

SELECT OC.FirstName, OC.LastName, OH.SalesOrderID
FROM Sales.SalesOrderHeader AS OH
JOIN Person.Contact AS OC
ON OH.ContactID = OC.ContactID
WHERE OC.FirstName LIKE 'John%'
OPTION (LOOP JOIN)

SELECT OC.FirstName, OC.LastName, OH.SalesOrderID
FROM Sales.SalesOrderHeader AS OH
JOIN Person.Contact AS OC
ON OH.ContactID = OC.ContactID
WHERE OC.FirstName LIKE 'John%'
OPTION (MERGE JOIN)

總結

Nested Loops

  • 複雜度: O(NlogM)。
  • 其中一個表很小的時候。
  • 較大的表允許使用索引查詢連線欄位。

Merge Join

  • 複雜度: O(N+M)。
  • 兩個輸入的連線欄位是有序的。
  • 使用=操作符
  • 適合非常大的表

Hash Match

  • 複雜度: O(N*hc+M*hm+J)
  • 最後預設的操作符
  • 使用雜湊表和動態雜湊匹配函式匹配行

     本篇隨筆詳細介紹了三種連結操作符和它們的觸發機制,當然這些也都是動態的,就像前面說的沒有最佳的操作符,只有最合適的,要根據實際請款選擇不同的操作符。

相關推薦

SQL連線操作符介紹迴圈, 匹配和合連線

  今天我將介紹在SQLServer 中的三種連線操作符型別,分別是:迴圈巢狀、雜湊匹配和合並連線。主要對這三種連線的不同、複雜度用範例的形式一一介紹。 簡介:什麼是連線操作符   連線操作符是一種演算法型別,它是SQLServer優化器為了實現兩個資料集合之間的邏輯連線選擇的操作符。優化器可以基

向標準輸出上列印一些用ASCII字元組成的圖形彙總迴圈關鍵是找要輸出物與行數n之間的關係,找規律

1 #include <stdio.h> int main() { int i,j,n; scanf("%d",&n);//有n行 for(i=1;i<=n;i++) {for(j=1;j<=2*

java基礎迴圈與控制跳轉

迴圈巢狀 package com.st.basis.day04; /* * 迴圈巢狀 * 外層控制行數 * 內層控制列數 * */ public class LoopNesting { public static void main(String[] args) { //

oracle 表連線方式: nested loop 迴圈和Hash Join的比較

一、建立兩張實驗用表:wireless_site.merchant和wireless_site.bb SQL> select count(*) from wireless_site.merchant;   COUNT(*) ----------      14005

vue v-for迴圈的探索

使用v-for迴圈的目的就是為了處理大量型別重複的資料,歸根結底是一種有規律的資料,但是有些規律卻不是那麼容易的,很多時候,我們會使用到迴圈,甚至多重迴圈的巢狀,不同的迴圈巢狀對應著不同的json資料的結構,本篇主要講述的是使用v-for迴圈解決部分同,部分不同的情況,主要是

微信小程式-跟隨選單樓梯效果迴圈載入資料

效果如圖: 程式碼如下: wxml //使用迴圈巢狀data資料格式寫對即可 <scroll-view class="left" scroll-y> <view wx:for="{{left}}" class="le

continue 在 switch中使用 switch在while迴圈

如果你在switch中使用continue,continue生效是對於while迴圈 如果你在switch中使用break,break生效是對於switch。 #include <stdio.h> int main() {     int i_index;

豬豬的IT之路---Java成長之路day04do...while,for,迴圈使用關鍵字,迴圈

迴圈: While(迴圈條件){ 迴圈體; } 迴圈條件為true時迴圈或重複執行迴圈體,直到迴圈條件為false迴圈結束 注意: 1.迴圈開始條件一般要定義在迴圈結構前面 2.迴圈操作(迴圈體)中一般都應該存在步進操作(++或–) do…while迴圈語句 do…while迴圈語句和whi

SQL WHILE 迴圈

摘錄自:http://imysqldba.blog.51cto.com/1222376/595433  - 內迴圈變數在選環後要重新賦初值,否則不能迴圈。 程式碼如下: DELIMITER $$ USE `qqfs_db_items`$$ DROPPROCEDURE 

思考專案 求1+2的2次方+3的3次方+4的4次方+5的5次方+6的6次方的值。異種迴圈

/*   * Copyright (c) 2014, 煙臺大學計算機學院   * All rights reserved.   * 檔名稱:test.cpp   * 作    者:劉暢    * 完成

MySQL用while實現for迴圈插入學生成績資訊功能

本次實驗的目的在於實現mysql中自動插入學生成績資訊,即給每個學生自動插入1~6號課程的成績, 屬性如下: 學生學號(i):1~23 課程號(j):1~6 成績:用rand()函式來實現自動填成績 此功能在java中用兩個for迴圈巢狀即可,程式碼如下: for(in

vue v-for 迴圈的探索

舉一個簡單的例子:1、結構部分內容(1)html 、css部分<div class="Classfily" v-for="(subtitle) in pdata.subtitle"><el-row><!--分類-->  <el-col

Lua 迴圈

Lua 程式語言中允許迴圈中嵌入迴圈。以下例項演示了 Lua 迴圈巢狀的應用。 語法 Lua 程式語言中 for 迴圈巢狀語法格式: for init,max/min value, increment do for init,max/min value, inc

for迴圈 案例列印各種形狀的星星

案例:各種形狀星星列印 //方形 /*    for(var i=0;i<6;i++){         for(var j=0;j<6;j++){ &

迴圈如何執行和switch break區別

for語句裡巢狀if語句 當for語句裡第一個條件不滿足if語句時,則繼續執行for迴圈,若滿足,執行if語句 例: for(i=0;i<=4;i++) for(j=0;j<=4;j++) if(i!=j) { for(k=0;k<

【pykafka】爬蟲篇:python使用python連線kafka介紹

本人菜雞,最近還更新python的爬蟲系列,有什麼錯誤,還望大家批評指出! 該系列暫時總共有4篇文章,連線如下: 【python】爬蟲篇:python連線postgresql(一):https://blog.csdn.net/lsr40/article/details/83311860

Java 知識點整理-10.Java集合框架 去除ArrayList中重複字串、自定義物件 棧和佇列 泛型 JDK5新特性 asList() toArray(T[] a) ArrayList迴圈

詳細標題:去除ArrayList中重複字串、自定義物件元素方式 棧和佇列 泛型 JDK5新特性(增強for迴圈 三種迭代(普通for、迭代器、增強for)中的元素能否刪除 靜態匯入 可變引數)Arrays工具類的asList() Collection中toArray(T[] a) 集合巢狀之Arra

for迴圈的使用

雙重迴圈排序     雙重迴圈排序演算法是一種非常簡單的排序演算法,這種演算法很容易讓人理解,也很實用,但是排序的效率很低。基本思路是用第一個數與後面的所有數進行比較,然後把最小的數放到第一個位置,然後用第二個數與後面的所有數進行比較,然後把第二個最小的數放到第二個位置,然後

兩種超詳細的遠端連線工具介紹從安裝到使用

眾所周知,個人電腦與伺服器不同,伺服器一般都是執行在IDC機房中,我們通常不會直接接觸到伺服器硬體,而是通過各種遠端方式對伺服器進行控制。於是遠端連線工具便應運而生了,下面簡單介紹一種常用的 linux 連線工具。 一、 Notepad++ Notepad++是一套非常有特色的自由

第11課 迴圈和演算法

一、迴圈巢狀  1、列印九九乘法表 for i in range(1, 10): # 行數 for j in range(1, i + 1): # print('{} * {} = {}'.format(j, i, i * j), end = '\t' ) #