2.表串連(Table join)
到目前為止我們都是在和單個表打交道,下邊我們看下查詢中的串連。下邊這個查詢擷取僱員資訊,把FirstName和LastName串連起來,這樣返回的資訊顯得更為友好。
SELECT e.[Title], a.[City], c.[LastName] + ', ' + c.[FirstName] AS EmployeeNameFROM [HumanResources].[Employee] eJOIN [HumanResources].[EmployeeAddress] ed ON e.[EmployeeID] = ed.[EmployeeID]JOIN [Person].[Address] a ON [ed].[AddressID] = [a].[AddressID]JOIN [Person].[Contact] c ON e.[ContactID] = c.[ContactID];
執行計畫
圖2-12
這個查詢中出現了多個的處理步驟,每個步驟的開銷也不同,它們在執行樹中從右到坐一步一步累計起來。其中3個開銷最大的操作分別是
1.對Person.Address表的索引掃描(Index Scan),45%
2.HumanResource.EmployeeAddress表和Person.Address表之間的Hash Match Join操作,28%
3.Person.Contact表上的叢集索引掃描,17%
右上方的是對HumanResource.EmployeeAddress表的索引掃描,它的下邊就是對Person.Address表的索引掃描,也就是我們的開銷最大的運算子。看下ToolTip,2-13,我們可以看到這是對索引IX_Address_AddressLine1_AddressLine2_City_StateProvinceID_PostalCode執行的掃描,儲存引擎遍曆了19614行的資料來找到我們需要的。
圖2-13
如Output list裡所示,查詢最佳化工具需要AddressId列和City列。查詢最佳化工具按照表中的索引和列的可選擇性進行計算,它認為最好的方式就是遍曆索引。遍曆19614行的資料的開銷佔據了總開銷的45%。
0.180413僅僅是個內部計算出的數字,最佳化器用于衡量各個操作的相對開銷,這個數字越低操作的效率越高。
Hash Match Join
繼續上邊的例子,這兩個索引掃描的輸出通過hash match join組合起來。
在我們討論Hash Match Join是什麼之前,需要瞭解兩個概念,Hash運算(即散列演算法) 和Hash表。
Hash運算是一種編程技術,用來把資料轉換為符號形式,使資料可以更容易更快速地被檢索。例如,表中的一行資料,可以通過程式轉化為一個唯一的值,這個值就代表了這行資料的內容。這和資料加密很類似,一個hash值同樣也可以被轉換回未經處理資料。
Hash表是一個資料結構,它把所有的元素都切分成同等大小的“類”或“塊”,允許對這些元素的快速存取。hash函數決定了元素應該進入哪個“塊”。例如,你可以從表裡取出一行資料,將其轉換為hash值,然後將hash值存入hash表。
Hash Match Join 發生時,Sql Server串連一大一小兩張表,對小表裡的資料行進行hash運算,將產生的hash值插入到hash表裡,然後遍曆大表裡的資料,每次取出一行在hash表裡尋找匹配的行。對小表進行hash運算是為了降低hash表的大小,hash值可以進行快速的比較。如果兩張表都很大,那麼hash match jion和其他類型的join比起來就非常低效。
Hash Match Join對大資料集尤其是其中一個表比另外的一個小很多的時候效率很高。對錶沒有按照join列排序或者表中沒有可用索引的情況,hash match join也是很有效方式。
Hash Match Join的出現也可能意味著存在更高效的串連方式(Nested Loop或Merge),這可能是以下原因造成的:
1.索引缺失或者索引不正確
2.where語句缺失
3.where語句裡有對索引列的計算或轉換使得索引失效
在這些情況下,最佳化器認為Hash Match Join是串連兩張表的最高效的方式,但是有可能可以通過增加索引、或者增加where語句來減少資料量等方式獲得更高效的查詢。
叢集索引尋找(Clustered Index Seek)
接下來我們看佔用17%的操作,是對Person.Contact表上的PK_Contact_ContactID叢集索引尋找,對於這個表來說PK_Contact_ContactID既是主鍵又是叢集索引。
圖2-15
從Seek Predicates節可以看出,這個操作直接將HumanResources.Employee表和Person.Contact表的ContactID列進行串連。
Nested Loop Join(嵌套迴圈串連)
圖2-16
顧名思義,Nested Loop Join這個操作使用嵌套雙層迴圈,在我們這個計劃裡下邊這個(17%那個)運算子的結果在外層。由於兩個資料集都很小,所以這是個很高效的操作。
只要內層資料集小,外層資料集(小不小不要緊)有索引的情況下,Nested Loop Join是一種非常高效的串連機制。除非資料集超大,這種串連方式應該是你最希望看到的串連方式。
標量計算(Compute Scarlar)
圖2-17
標量計算表示此操作產生了一個標量值,通常是通過計算。我們這個例子中,假名EmployeeName組合了LastName和FirstName並用逗號串連。
Merge Join
除了Hash Match 和Nested Loop外,還有一種串連即Merge Join。執行以下查詢。
SELECT c.CustomerIDFROM Sales.SalesOrderDetail odJOIN Sales.SalesOrderHeader ohON od.SalesOrderID = oh.SalesOrderIDJOIN S ales.Customer cON oh.CustomerID = c.CustomerID
執行計畫
圖2-18
使用Merge Join串連的兩張表必須是按照串連列預先排序好了的,這中情況下Merge Join是一種高效的串連方式。如果串連的表沒有按照串連列預先排序的話,查詢最佳化工具要麼先排序再執行Merge
Join, 或者執行效率稍低些的Hash Match Join。
PS:以前做cobol時處理資料的一種方式,估計原理是一樣的。有兩組資料,都是排好序的,假設是按某個欄位從小到大。設兩個指標分別代表左右兩個資料的目前記錄位置,比較當前位置的資料大小,如果左邊的小右邊大,那麼左邊的指標向前移動一位,如果右邊的小左邊的大,那麼右邊的指標向前移動一位,如果一樣大,那麼這是匹配的兩條資料,左右同時向前移動一位。這隻是串連列的值唯一的情形,也可能是多對多的匹配,當多對多匹配時,Merge Join就必須使用cache了,原理應該是這樣子,這樣就避免了雙層迴圈,所以如果是排好序的兩張表串連,Merge Join的效率應該是最高的。