SQL Server 2005 hash聯接演算法

2021-09-05 21:39:21 字數 4385 閱讀 2777

如果兩個聯接輸入都很大,而且這兩個輸入的大小差不多,則預先排序的合併聯接提供的效能與雜湊聯接相近。但是,如果這兩個輸入的大小相差很大,則雜湊聯接操作通常快得多。

雜湊聯接可以有效處理未排序的大型非索引輸入。它們對複雜查詢的中間結果很有用,因為: ·

中間結果未經索引(除非已經顯式儲存到磁碟上然後建立索引),而且通常不為查詢計畫中的下乙個操作進行適當的排序。 ·

查詢優化器只估計中間結果的大小。由於對於複雜查詢,估計可能有很大的誤差,因此如果中間結果比預期的大得多,則處理中間結果的演算法不僅必須有效而且必須適度弱化。

hash join一般用於一張小表和一張大表進行join時。hash join的過程大致如下(下面所說的記憶體就指sort area,關於過程,後

面會作詳細討論):

1.  一張小表被hash在記憶體中。因為資料量小,所以這張小表的大多數資料已經駐入在記憶體中,剩下的少量資料被放置在臨時表空間中;

2.  每讀取大表的一條記錄,就和小表中記憶體中的資料進行比較,如果符合,則立即輸出資料(也就是說沒有讀取臨時表空間中的小表的數

據)。而如果大表的資料與小表中臨時表空間的資料相符合,則不直接輸出,而是也被儲存臨時表空間中。

3.  當大表的所有資料都讀取完畢,將臨時表空間中的資料以其輸出。

如果小表的資料量足夠小(小於hash area size),那所有資料就都在記憶體中了,可以避免對臨時表空間的讀寫。

如果是並行環境下,前面中的第2步就變成如下了:

2.每讀取一條大表的記錄,和記憶體中小表的資料比較,如果符合先做join,而不直接輸出,直到整張大表資料讀取完畢。如果記憶體足夠,

join好的資料就儲存在記憶體中。否則,就儲存在臨時表空間中。

1,處理大量、未排序、無索引的資料

2 ,hash join乙個較大限制是它只能應用於等值聯結(equality join),這主要是由於雜湊函式及其桶的確定性及無序性所導致的。

我們在這裡看看blog中的例子:

原貼:

這個是乙個sql牛人的 部落格,他在上面提出了乙個小問題:

use tempdb

gocreate table b1 (blat1 nchar(

5 ) not null)

create table b2 (blat2 varchar(

200 ) not null)

goinsert b1

select left(addressline1, 

5 ) as blat1 from adventureworks.person.address

insert b2

select addressline1 as blat2

from adventureworks.person.address

go然後執行如下的查詢語句:

select 

* from b1  

join b2 on    b2.blat2 like b1.blat1 +'

%'上面牛人blog提出的問題,不建立任何物件(索引,索引檢視等),優化這個語句。其實這就是考察hash聯結的使用。

分析語句:

1. 選擇演算法:

1,由於hash聯結只能用相等聯結才能使用,這時兩個表b1,b2通過like來聯結,結果肯定不會是hash聯結

2,由於這裡的聯結是inner join 而這時使用merge聯結同樣需要兩個字段關聯查詢,結果肯定不會是merge聯結

3,由於3個聯結2個不滿足條件,這時優化器只能採用loop巢狀迴圈聯結

2,如何執行loop聯結

通過上面的分析,我們已經看到了只能採用loop巢狀迴圈。b1,b2都沒有索引。

假如b1有1萬條,b2有2萬條。

看看執行結果:根據巢狀迴圈演算法,是兩個迴圈。由於都沒有索引,系統先表掃瞄b1表1萬,在通過逐條表掃瞄b2兩萬條。

最少io總

數: 1萬(b1的行數)*表b2的掃瞄io次數+b1表掃瞄io次數。

測試上面io次數計算例子:

表 [zping.com]中有1000條記錄,id為唯一32id,通過id插入資料到b1,b2中。兩個表各寫入1000行,返回io數:

( 1002

行受影響)表 '

worktable

'。掃瞄計數 

2,邏輯讀取 

12068

次,物理讀取 

0次,預讀 

0次,lob 邏輯讀取 

0次,lob 物理讀取 

0次,lob 預讀 

0 次。表 '

b2'。掃瞄計數 

2,邏輯讀取 

16次,物理讀取 

0次,預讀 

0次,lob 邏輯讀取 

0次,lob 物理讀取 

0次,lob 預讀 

0 次。表 '

b1'。掃瞄計數 

3,邏輯讀取 

4次,物理讀取 

0次,預讀 

24次,lob 邏輯讀取 

0次,lob 物理讀取 

0次,lob 預讀 

0 次。表 '

worktable

'。掃瞄計數 

0,邏輯讀取 

0次,物理讀取 

0次,預讀 

0次,lob 邏輯讀取 

0次,lob 物理讀取 

0次,lob 預讀 0次。

按照上面的公式:4+1002*8=8020次io 和這裡io次數比較比較起來,差距較大。

可以看出,io數要比上面的公式大。

如果要優化就不能使用巢狀迴圈演算法 。一些網友也給出了解決辦法:最優的方法是:

select *

from (select blat1 s from b1) a 

join (select left(blat2,

5 ) s from b2) b

on a.s

=b.s

如果換成了 

select *

from (select distinct blat1 s from b1) a 

join (select distinct left(blat2,

5 ) s from b2) b

on a.s

=b.s

對比成本,發現hash聯結要比merge聯結要快,因為這裡有乙個distinct顯示排序操作,資料庫會自動採取合併演算法。

因為有了排序操作,成本就比hash慢。

因此最佳優化的sql語句:

select *

from (select blat1 s from b1) a 

join (select left(blat2,

5 ) s from b2) b

on a.s

=b.s

今天又看了一下阿里巴巴的dba,在oracle裡的研究hash聯結的優化和例子:

他得出的結論:

1. hash的時候一定要用小記錄集做驅動.

2. 大/小記錄集作驅動時, 讀取資料檔案的cost兩者是一樣的.但是前者的記錄集在大到一定程度的時候, 在構建hash桶會產生很多物理讀, 而且這些物理讀根本無法消除, 每次執行都會產生.

我們測試一下,看看不同的驅動表對hash聯結的效能影響:

表workflowinfo1 有50多萬條記錄,表workflowlog1有70多萬資料。

執行一下語句:  

select 

* from dbo.workflowinfo1 a

inner hash join dbo.workflowlog1 b on a.workflowid

= b.workflowid

select 

* from dbo.workflowlog1 a

inner hash join dbo.workflowinfo1 b on a.workflowid

=b.workflowid

乙個是以

workflowinfo1為驅動表,乙個是以workflowlog1為驅動表。對比成本:

這時發現以小的驅動表(workflowinfo1,50多萬)成本較少。而多的驅動表(workflowlog1,70多萬)成本較高。

1,hash聯結適合輸入和輸出都是大型資料集的情況。

2,聯結列必須相等聯結,(不相等可以如上方法換成相等聯結)

3,使用較少的表為驅動表。(在使用hash提示聯結尤其注意)

SQL Server2005複製實現

一 準備工作 1 在發布伺服器上建立乙個共享目錄,作為發布快照檔案的存放目錄。例如 在d 盤根目錄下建資料夾名為pub 2 設定sql 發布伺服器和訂閱伺服器均設定 步驟 開啟服務 控制面板 管理工具 服務 右擊sqlserver agent 屬性 登入 選擇 此帳戶 輸入或選擇第一步中建立的win...

SQL Server 2005完全解除安裝

sql server 2005的解除安裝是乙個非常頭疼的問題。我曾經嘗試過直接使用 新增或刪除程式 工具解除安裝 清除安裝目錄 刪除登錄檔內容等等各種方式綜合解除安裝,勉強成功。現在終於找到了乙個事半功倍的方法,多次嘗試,未有失敗,具體如下 第一種是微軟官方提供的工具 msicuu2.exe 微軟官...

SQLSERVER 2005 遞迴查詢

專案中有使用者組表usergroup如下 其中pid表示當前組的上級組 表資料如下 現在想查詢出頂級組 沒有上級組叫頂級組 a1組的所有子孫組id,sql如下 查詢子節點 with rtd1 as select id pid from usergroup rtd2 as select from rt...