提示:看到很多朋友對數據庫的理解、認識還是沒有突破一個瓶頸,而這個瓶頸往往只是一層窗紙,越過了你將看到一個新世界。
2. merge join
merge join第一個步驟是確保兩個關聯表都是按照關聯的字段進行排序。如果關聯字段有可用的索引,並且排序一致,則可以直接進行merge join操作;否則,SQL Server需要先對關聯的表按照關聯字段進行一次排序(就是說在merge join前的兩個輸入上,可能都需要執行一個Sort操作,再進行merge join)。
兩個表都按照關聯字段排序好之後,merge join操作從每個表取一條記錄開始匹配,如果符合關聯條件,則放入結果集中;否則,將關聯字段值較小的記錄拋棄,從這條記錄對應的表中取下一條記錄繼續進行匹配,直到整個循環結束。
在多對多的關聯表上執行merge join時,通常需要使用臨時表進行操作。例如A join B使用merge join時,如果對於關聯字段的某一組值,在A和B中都存在多條記錄A1、A2...An、B1、B2...Bn,則為A中每一條記錄A1、A2...An,都必須在B中對所有相等的記錄B1、B2...Bn進行一次匹配。這樣,指針需要多次從B1移動到Bn,每一次都需要讀取相應的B1...Bn記錄。將B1...Bn的記錄預先讀出來放入內存臨時表中,比從原數據頁或磁盤讀取要快。
merge join操作本身是非常快的,但是merge join前進行的排序可能會相當耗時(SQL Server最消耗內存和CPU的操作,一個是大數據排序,一個是大數據的hash運算,這都是指查詢計劃裡面的Sort以及Hash相關的操作,例如hash join、使用hash算法實現的Distinct操作等,而不是指你的SQL中order by關鍵字),尤其是對數據量非常大的記錄集,因此導致使用merge join的查詢成本變得非常高。對於數據量非常大的表,如果merge join的關聯字段可以使用聚集索引,merge join是最快的Join方法之一。因此優化方案是在表結構設計層面良好的設計關聯關系和表的索引結構,SQL語句充分利用索引,盡可能減少merge join前的排序操作,減少Bookmark Lookup操作。
一般情況下,如果無法滿足nested loop條件,會考慮對merge join方法的評估。merge join的選擇,主要是考慮兩個輸入的數據量,以及分別對應於關聯字段是否能夠命中索引。例如tableA join tableB,關聯字段在兩個表中都能命中索引,數據量超過了nested loop的選擇范圍,則會考慮使用merge join方法。當然,如果tableA和tableB的數據量過大導致評估出來的成本過高,則會放棄merge join而評估hash join了。
使用inner merge join或者option(merge join)強制使用merge join方法。
3. hash join
hash join有兩個輸入:build input(也叫做outer input)和probe input(也叫做inner input),不僅用於inner/left/right join等,象union/group by等也會使用hash join進行操作,在group by中build input和probe input都是同一個記錄集。
同nested loop,在執行計劃中build input位於上方,probe input位於下方。
hash join操作分兩個階段完成:build(構造)階段和probe(探測)階段。