目錄
一、簡介
Oracle中主要有下面四種表連接方式:
- SORT MERGE JOIN(排序-合併連接);
- NESTED LOOPS(嵌套循環);
- HASH JOIN(哈希連接);
- CARTESIAN PRODUCT(笛卡爾積);
Oracle中,通過JOIN關鍵字進行表連接操作,一次只能連接兩張表,JOIN 操作的各步驟一般是串行的(在讀取做連接的兩張表的數據時可以並行讀取)。
需要說明兩個重要的概念:驅動表與匹配表。注意這些概念只存在於NESTED LOOPS(嵌套循環)與 HASH JOIN(哈希連接)兩種表連接方式中。
- 驅動表(Driving Table):
表連接時首先存取的表,又稱外層表(Outer Table),可以簡單理解爲查詢的主表;
注意:如果驅動表返回的數據行數過多,必須會影響查詢效率,所以我們一般選擇小表(查詢結果返回較少行數的表) 作爲驅動表。
- 匹配表(Probed Table):
表連接時後面存取的表,又稱爲內層表(Inner Table),也叫被驅動表,可以簡單理解爲查詢的副表;
從驅動表獲取一行具體數據後,會到該表中尋找符合連接條件的行。故該表一般爲大表(查詢結果返回較多行數的表)。
下面通過一些示例對每一種表連接方式進行詳解。
二、 SORT MERGE JOIN(排序-合併連接)
內部連接過程:
- 第一步:生成驅動表(主表)需要的數據,按照連接操作關聯列對這些數據進行排序;
- 第二步:生成匹配表(附表)需要的數據,按照與上面一步中對應的連接操作關聯列對數據進行排序;
- 第三步:兩邊已排序的行放在一起執行合併操作(對兩邊的數據集進行掃描並判斷是否連接);
示例:
explain plan for select * from zhxg_zhcp_cpjgb t1
join zhxg_xsxx_xsjbxx t2
on t1.xsid > t2.PKID;
select * from table(dbms_xplan.display);
執行計劃:
通過上述執行計劃,可見查詢使用到了排序-合併連接方式。
注意事項:排序-合併連接的表無驅動順序,兩個表是對等的,哪個表在前面都可以,可以考慮在操作關聯列上建立索引讓其能預先排好序,連接速度可大大提高。排序合併連接其實很耗費資源,因爲要對2個表/結果集進行排序,所以一般情況下,CBO是不會選擇走SORT MERGE JOIN的。
應用場景:當結果集已經排過序;
適用的連接條件有: <、<=、=、>、>=
不適用的連接條件有: <> 、like
三、NESTED LOOPS(嵌套循環)
內部連接過程:
- a) 第一步:取出主表(驅動表)的第一行數據,遍歷副表(匹配表)的所有行並檢查是否有匹配的,取出匹配的行放入結果集中;
- b) 取出主表(驅動表)的第二行數據,遍歷副表(匹配表)的所有行並檢查是否有匹配的,取出匹配的行放入結果集中;
- c) 若主表(驅動表)中返回了 N 行數據,則副表(匹配表)也相應的會被全表遍歷 N 次;
示例:
zhxg_zhcp_cpjgb數據表的記錄數爲100多條;
zhxg_xsxx_xsjbxx數據表的記錄數爲6000多條;
下面先以記錄數較少的zhxg_zhcp_cpjgb作爲驅動表進行測試,觀察執行計劃。
explain plan for
select /*+ leading(t1) use_nl(t2) */
*
from zhxg_zhcp_cpjgb t1
join zhxg_xsxx_xsjbxx t2
on t1.xsid = t2.PKID;
select * from table(dbms_xplan.display);
執行計劃:
下面以記錄數較多的zhxg_xsxx_xsjbxx作爲驅動表進行測試,觀察執行計劃。
示例:
explain plan for
select /*+ leading(t2) use_nl(t1) */
*
from zhxg_zhcp_cpjgb t1
join zhxg_xsxx_xsjbxx t2
on t1.xsid = t2.PKID;
select * from table(dbms_xplan.display);
執行計劃:
可見,這裏以zhxg_xsxx_xsjbxx作爲驅動表測試的話,可能是因爲zhxg_xsxx_xsjbxx表pkid是主鍵,所以這裏顯示的並不是嵌套循環,而是下面我們即將介紹的哈希連接方式。
注意事項:
因爲主表(驅動表)的每一行都會去匹配 副表(匹配表)的所有行,所以當主表(驅動表)返回的行數儘可能少並且能高效訪問副表(匹配表)(如建立適當的索引)時,效率較高。應儘可能使用限制條件(Where過濾條件)使驅動表返回的行數儘可能少,同時在匹配表的連接操作關聯列上建立唯一索引(UNIQUE INDEX)或是選擇性較好的非唯一索引,此時嵌套循環連接的執行效率會變得很高。若驅動表返回的行數較多,即使匹配表連接操作關聯列上存在索引,連接效率也不會很高。
小總結:
- 嵌套循環有驅動表和被驅動表的概念,驅動順序不同執行計劃差異非常大;
- 驅動表只被訪問一次,被驅動表被訪問多次。嵌套循環訪問表的次數直接受驅動表的返回記錄數的影響。因此應當讓實際返回記錄數(A-Rows)小的表作爲驅動表,返回記錄數大的表作爲被驅動表;
- 在驅動表的查詢條件上建立索引可以改善查詢效率;
- 在連接條件上建立索引也可以改善查詢效率;
- 在被驅動表查詢條件建立索引對查詢效率的影響要視情況而定,不一定帶來好處;
四、HASH JOIN(哈希連接)
內部連接過程:
- a) 取出主表(驅動表)的數據集,然後將其構建成內存中的一個 Hash Table(Hash函數的Hash KEY就是連接操作關聯列),創建Hash位圖(bitmap);
- b) 取出副表(匹配表)的數據集,對其中的每一條數據的連接操作關聯列使用相同的Hash函數並找到對應的 a) 裏的數據在 Hash Table 中的位置,在該位置上檢查能否找到匹配的數據;
示例:
explain plan for select /*+ leading(t1) use_hash(t2)*/
*
from zhxg_zhcp_cpjgb t1
join zhxg_xsxx_xsjbxx t2
on t1.xsid = t2.pkid;
select * from table(dbms_xplan.display);
執行計劃:
小總結:
- 驅動表和被驅動表都是最多隻被訪問一次;
- 哈希連接的表有驅動順序;
- 哈希連接不適用於的連接條件是:不等於<>,大於>,小於<,小於等於<=,大於等於>=,like;
五、CARTESIAN PRODUCT(笛卡爾積)
當兩個數據表做連接,但是它們之間沒有關聯條件時,就會在兩個row source中做笛卡兒乘積,這通常由編寫代碼疏漏造成(即程序員忘了寫關聯條件)。笛卡爾乘積是一個表的每一行依次與另一個表中的所有行匹配。在特殊情況下我們可以使用笛卡兒乘積,如在星形連接中,除此之外,我們要儘量使用笛卡兒乘積,否則,查詢效率會非常慢。
假如表t1有n行,t2表有m行,笛卡爾乘積的結果就是得到n * m行結果。
explain plan for select /*+ leading(t1) use_hash(t2)*/
*
from zhxg_zhcp_cpjgb t1, --忘記寫關聯關係
zhxg_xsxx_xsjbxx t2;
select *
from table(dbms_xplan.display);
執行計劃:
觀察上面的執行計劃,CARTESIAN關鍵字指出了在2個表之間做笛卡爾乘積。想一下,如果兩個表的數據量都非常大,那麼查詢效率可想而知。
六、參考資料
https://blog.csdn.net/waterxcfg304/article/details/25873265
https://blog.csdn.net/biww620/article/details/73477435/
https://www.cnblogs.com/Dreamer-1/p/6076440.html
https://blog.csdn.net/waterxcfg304/article/details/25872505