如果兩個聯接輸入都很大,而且這兩個輸入的大小差不多,則預先排序的合併聯接提供的效能與雜湊聯接相近。但是,如果這兩個輸入的大小相差很大,則雜湊聯接操作通常快得多。
雜湊聯接可以有效處理未排序的大型非索引輸入。它們對複雜查詢的中間結果很有用,因為: ·
中間結果未經索引(除非已經顯式儲存到磁碟上然後建立索引),而且通常不為查詢計畫中的下乙個操作進行適當的排序。 ·
查詢優化器只估計中間結果的大小。由於對於複雜查詢,估計可能有很大的誤差,因此如果中間結果比預期的大得多,則處理中間結果的演算法不僅必須有效而且必須適度弱化。
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...