1. 程式人生 > >PLSQL高效設計之exists和in

PLSQL高效設計之exists和in

<![endif]--> <![endif]-->

發現公司同事很喜歡用existsin 做子查詢關聯,我覺得很有必要研究下

兩者的區別,供參考和備忘

/* (這段資訊來自網路begin )對於in 的使用,就相當於對inner table 執行一個帶distinct 的子查詢,然後將得到的結果集再和outer table 進行外連線,連線方式和索引使用任然同於普通兩表的連線(這段資訊來自網路end*/

對於網路的這段描述,我給予了測試,測試表為 liomuser.staff ,和liomuser.department ,這兩張表都是小表,數量在1 萬左右。

-- 例如:

select *

from liomuser.staff

where department_id in ( select department_id from liomuser.department);

-- 可以轉換為

select a.*

from liomuser.staff a,

( select distinct department_id from liomuser.department) b

where a.department_id = b.department_id;

執行計劃分別如下:

1 select *

from liomuser.staff

where department_id in ( select department_id from liomuser.department);

2 select a.*

from liomuser.staff a,

( select distinct department_id from liomuser.department) b

where a.department_id = b.department_id;

我選擇的是兩個小表,從資料上看採用外連線的方式除了一致性讀要稍微小一點,兩者執行計劃和統計資訊幾乎一樣。

測試結果顯示對於小表網路上給出的描述是正確的

但是以我的經驗,in 的方式應該比外連線效能要差很多,按照上面的測試,兩者似乎是一樣的執行路徑,是不是應為表資料量少的緣故呢?

我決定採用兩張大表做測試,cust_orderorder_detail 這兩張表的資料量都在一千萬以上。

首先測試in ,語句如下:

select a.*

from liomuser.cust_order a

where order_id in ( select order_id from liomuser.order_detail b);

執行計劃如下:

測試2 外連線,語句如下:

select a.*

from liomuser.cust_order a,

( select distinct order_id from liomuser.order_detail) b

where a.order_id = .order_id ;

執行計劃如下:

對著兩個大表的in 和外連線的對比可以看出,採用外連線的執行計劃明顯優於in 的方式,採用in 方式則表連線採用nested loop 方式,而外連線採用了HASH JOIN

並且in 方式的CPUcost 要比外連線大1/3, 這樣看來,對於小表,或者說inner table 是小表的查詢,in 和外連線都差不多,但是對於大表,特別是inner table 資料量巨大的時候,採用外連線要好很多。

由此看出,in 並不完全等同於與inner tabledistinct 外連線,但是外連線要比in 有效率得多。

下面討論下 EXIST

實際上exists 等同於先對outer table 進行掃描,從中檢索到每行和inner table 做迴圈匹配,執行計劃如下:

註釋:部分網上資料認為existsouter table 會進行全表掃描,但是在該執行計劃中沒有發現全表掃描,仍然走了索引。

Exists 的寫法可以轉換成:

declare cnt number ( 10 );

for cur in ( select a.*from liomuser.cust_order a) loop

cnt:= 0 ;

select count ( 1 ) into cnt from liomuser.order_detail where order_id=cur.order_id;

if cnt<> 0 then

return cur;

end if ;

end loop ;

exists in 的比對:

語句 1 in

語句 2 exsits

從執行計劃上來看沒有任何區別,再讓我們看看執行的統計資訊:

語句 1 in

select a.*

from liomuser.cust_order a

where order_id in ( select order_id from liomuser.order_detail b)

語句 2 exists

select a.*

from liomuser.cust_order a

where exists

( select 1 from liomuser.order_detail b where a.order_id = b.order_id)

從兩種方式統計資訊可以看出,採用 exists 的一致性讀要比 in 要好,但是 bytessent 要比 in 高,這個也從側面驗證了前面所說的 exists 相當於 loop

通過上面解釋現在很容易明白當 inner table 資料量巨大且索引情況不好 ( 大量重複值等 ) 則不宜使用產生對 inner table 檢索而導致系統開支巨大 IN 操作,建議對 innertable 過大的查詢,採取 exsits ,或者外連線方式

另外: NOT IN 子句將執行個內部排序和合並 . 無論在哪種情況下 ,NOT IN 都是

最低效 ( 它對子查詢中表執行了個全表遍歷 ). 為了避免使用 NOT IN , 我們可以把它改寫成外連線 (Outer Joins) NOT EXISTS