1. 程式人生 > >MS SQL SERVER 中merge join合併連線介紹(轉)

MS SQL SERVER 中merge join合併連線介紹(轉)

1概述
Merge join 合併連線。兩個集合進行merge join,需要有一個等值的條件,然後需要兩個已排序好的集合。

2 one-to-many與many-to-many
2.1 One-to-many
當參與merge join的兩個集合中,其中一個集合在等值條件上是具有唯一性(如SELECT * FROM T1 INNER JOIN T2 ON T1.A=T2.B,如果T1在A列上具有唯一性),那麼即為one-to-many。主要步驟為:首先從兩個集合中各取出一條記錄進行比較,如果符合join條件,那麼取出該行;否則將值小記錄從集合中移除,然後取值小集合的下一行,繼續比較。
2.2 many-to many
當參與merge join的兩個集合中,沒有一個集合在等值條件上具有唯一性時,則採用many-to-many(SELECT * FROM T1 INNER JOIN T2 ON T1.A=T2.B,當列A與列B都不具有唯一性)。主要步驟為:在A和B中都存在A1,A2..An,B1,B2..Bn,那麼正常情況下需要為A的每一條記錄(A1,A2..An)都要將B中的B1,B2..Bn讀取出來,這樣浪費效能。所在資料庫在處理時,將B中的匹配行儲存在tempdb中,如果A中的下一行相等,則讀取tempdb中的內容,否則刪除tempdb中的資料。
2.3 one-to-many與many-to-many的比較
很顯然,one-to-many的效率更高,因為它不需要臨時表。那麼如何讓查詢優化器知道我們其中某個集合具有唯一性呢。方法一是:建立聚集索引;二是如distinct、group by操作符。

3排序與索引


資料庫幾個大的操作之一就是大表的排序,所以使用merge join如果表資料量比較大,並且無索引,那麼並不適合merge join。所以當資料量很大,就需要為其新增索引。
4示例
測試資料

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[GoodsType]'))
DROP TABLE [dbo].[GoodsType]
GO
--商品型別表
CREATE TABLE dbo.[GoodsType]
(
    id int,
    good_type_name nvarchar(50)
);

INSERT INTO dbo.GoodsType
SELECT 1,'服裝'
UNION ALL
SELECT 2,'數碼'
UNION ALL
SELECT 3,'家電'

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Goods]'))
DROP TABLE [dbo].[Goods]
GO
--商品型別表
CREATE TABLE dbo.[Goods]
(
    id int,
    good_name nvarchar(50),
    good_type int
);

INSERT INTO dbo.Goods
SELECT 1,'ADT恤',1
UNION ALL
SELECT 2,'AD外套',1
UNION ALL
SELECT 3,'T002電視',2
UNION ALL
SELECT 4,'海爾洗衣機',2
UNION ALL
SELECT 5,'HP222',3

4.1
未建任何索引,執行SQL

SET STATISTICS PROFILE ON
SELECT * 
FROM Goods AS G
    INNER JOIN GoodsType AS GT ON G.good_type=GT.id
OPTION(MERGE JOIN)

說明
1>未建立索引時,需要為兩個集合進行排序;
2>雖然在連線條件上唯一,但是未建唯一聚集索引時,為多對多的連線;

4.2
建立非聚集索引,執行SQL

CREATE CLUSTERED INDEX GT ON GOODSTYPE(ID)
CREATE CLUSTERED INDEX G ON GOODS(good_type)

SET STATISTICS PROFILE ON SELECT * FROM Goods AS G INNER JOIN GoodsType AS GT ON G.good_type=GT.id OPTION(MERGE JOIN)

說明:
1>建立索引後,執行merge join無排序的開銷
2>雖然兩個集合都建立了索引,並且連線的關鍵字也無重複,但還是多對多的連線,因為優化器不知道它是唯一的。

4.3
為其中一個集合建立唯一聚集索引,執行SQL

DROP INDEX GT ON GOODSTYPE
CREATE UNIQUE CLUSTERED INDEX GUT ON GOODSTYPE(ID)

SET STATISTICS PROFILE ON
SELECT * 
FROM Goods AS G
    INNER JOIN GoodsType AS GT ON G.good_type=GT.id
OPTION(MERGE JOIN)

說明:
1>為其中的一個集合建立唯一聚集索引時,此時的連線為一對一的連線(執行計劃中無一對一連線的概念)

5總結
當不適合使用nested join時,可以考慮使用merge join。在使用merge join時,需要注意兩個概念:一是排序,最好是索引排序,否則大資料量的實時排序會增加太多的成本;二是連線方式,是一對多還是多對多,如果關鍵字不重複,可以建立唯一聚集索引,即儘量使用一對多的連線。

其中set statistics profile on //開啟執行計劃,執行計劃的列說明如下:

Rows:執行計劃的每一步返回的實際行數。

Executes:執行計劃的每一步被運行了多少次。

StmtText:執行計劃的具體內容。執行計劃以一棵樹的形式顯示。每一行,都是執行的一步,都會有結果集返回,也都會有自己的cost。

EstimateRows:SQL Server根據表格上的統計資訊,預估的每一步的返回行數。在分析執行計劃時,我們會經常將Rows和EstimateRows這兩列做對比,先確認SQL Server預估得是否準確。
EstimateIO:SQL Server根據EstimateRows和統計資訊裡記錄的欄位長度,預估的每一步會產生的I/O cost。

EstimateCPU:SQL Server根據EstimateRows和統計資訊裡記錄的欄位長度,以及要做的事情的複雜度,預估的每一步會產生的CPU cost。

TotalSubtreeCost:SQL Server根據EstimateIO和EstimateCPU通過某種計算公式,計算出的每一步執行計劃子樹cost(包括這一步自己的cost和它的所有下層步驟的cost總和)。

Warnings:SQL Server在執行每一步時遇到的警告,例如,某一步沒有統計資訊支援cost預估等。

Parallel:執行計劃的這一步是不是使用了並行的執行計劃。