oracle 表連線方式: nested loop 巢狀迴圈和Hash Join的比較
一、建立兩張實驗用表:wireless_site.merchant和wireless_site.bb
SQL> select count(*) from wireless_site.merchant;
COUNT(*) ---------- 14005
SQL>
SQL> select count(*) from wireless_site.clickthroughrate;
COUNT(*) ---------- 2384026
SQL> create table wireless_site.bb as select * from wireless_site.clickthroughrate;
Table created.
SQL> SQL> select count(*) from wireless_site.bb;
COUNT(*) ---------- 2384026
SQL>
二、執行SQL語句並檢視sql語句的真實執行計劃:
SQL> alter session set statistics_level=all;
Session altered.
SQL>
2.1、首先檢視Hash Join的執行計劃:
執行SQL語句:select * from wireless_site.merchant,wireless_site.bb where bb.recordtype = merchant.merchantid and merchant.merchantid like '%3210%';
等待SQL語句執行完畢,然後再使用這條SQL語句:select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));檢視真實執行計劃:
由以上執行計劃可以看出:
1、HASH JOIN的驅動表為BB,掃描方式為全表掃描,執行了1次,實際返回了1658行
2、HASH JOIN的被驅動表為MERCHANT,掃描方式為全表掃描,執行了1次,實際返回了12行
3、需要注意的是:HASH JOIN的驅動表被被驅動表都會被掃描一次,而nested loop則是驅動表掃描一次,被驅動表被掃描N次(具體N的值是根據驅動表返回的行數來決定的。)
2.2、接著使用HINT的方式讓優化器強制走nested loop(使用wireless_site.merchant作為驅動表)
執行SQL語句:select /*+ leading(merchant) use_nl(bb) */ * from wireless_site.merchant,wireless_site.bb where bb.recordtype = merchant.merchantid and merchant.merchantid like '%3210%';
等待SQL語句執行完畢,然後再使用這條SQL語句:select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));檢視真實執行計劃:
此時,wireless_site.merchant作為驅動表,加上過濾條件filter後,實際返回行數為:
由以上執行計劃可以看出:
1、巢狀迴圈的驅動表為merchant,採用的掃描方式為全表掃描,執行了1次,返回了12行
2、巢狀迴圈的被驅動表為bb,採用的掃描方式是全表掃描,執行了12次,實際返回了1658行
2.3、然後再使用HINT的方式讓優化器強制走nested loop(使用wireless_site.bb作為驅動表)
執行SQL語句:select /*+ leading(bb) use_nl(merchant) */ * from wireless_site.merchant,wireless_site.bb where bb.recordtype = merchant.merchantid and merchant.merchantid like '%3210%'; 等待SQL語句執行完畢,然後再使用這條SQL語句:select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));檢視真實執行計劃:
此時,wireless_site.bb作為驅動表,加上過濾條件filter後,實際返回行數為:
由以上執行計劃可以看出:
1、表BB作為驅動表,掃描方式是全表掃描,實際返回行數為1658
2、表merchant作為被驅動表,採用的掃描方式為索引唯一掃描,執行了1658次,返回了1658行
3、被驅動表merchant索引掃描完成後,通過rowid回表讀數執行了1658次,實際返回了1658行
2.4、接下來在wireless_site.bb表中的recordtype列上建立索引
SQL> create index wireless_site.bb_idx on wireless_site.bb(recordtype);
Index created.
SQL>
接著使用HINT的方式讓優化器強制走nested loop(使用wireless_site.merchant作為驅動表):
執行SQL語句:select /*+ leading(merchant) use_nl(bb) */ * from wireless_site.merchant,wireless_site.bb where bb.recordtype = merchant.merchantid and merchant.merchantid like '%3210%';
等待SQL語句執行完畢,然後再使用這條SQL語句:select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));檢視真實執行計劃:
由執行計劃可以看出:
1、merchant作為驅動表,採用的是全表掃描的方式,實際返回行數為12
2、被驅動表BB的索引採用的是Index Range Scan的方式,實際返回行數為1658,共執行了12次
3、被驅動表BB索引被訪問了12次,每次掃描完索引後又根據rowid回表讀數(TABLE ACCESS BY INDEX ROWID),這樣BB表就被訪問了1658次
對比之前沒有在wireless_site.bb表中的recordtype列上建立索引時的執行計劃可以看出,建立索引後SQL花費不到1s的時間就可以出結果;沒有建立索引時,需要花費1分32秒左右。