原本執行迅速的查詢,在加入一個看似無害的條件 member.site = 'test' 後,執行時間暴增。SQL 如下:
SELECT uidTable.column_value,
first_name || ' ' || last_name,
company,
job_title,
UPPER(member_level),
UPPER(service_value)
FROM (SELECT * FROM TABLE(CAST(MULTISET(SELECT b FROM bbb) AS Taaa) FROM dual)) uidTable,
member
WHERE uidTable.column_value = member.login_id(+)
AND member.site = 'alibaba'
AND member.site = 'test';
原本驅動表是 uidTable(最多 1024 行),加入條件後,優化器將 member 表(600 萬行)作為驅動表,導致全表掃描,性能急劇下降。
為什麼外連接的連接順序會改變?因為外連接的連接順序並非由 COST 決定,而是由連接條件決定。當在外連接的表(member)上直接加上 member.site = 'test' 這樣的過濾條件時,外連接會隱性轉換為內連接,導致優化器重新排序。
| Id | Operation | Name | Rows | Bytes | Cost |
|---|---|---|---|---|---|
| 0 | SELECT STATEMENT | 1018 | 72278 | 8155 | |
| 1 | NESTED LOOPS | 1018 | 72278 | 8155 | |
| 2 | VIEW | 4072 | 69224 | 11 | |
| 3 | COLLECTION ITERATOR SUBQUERY FETCH | ||||
| 4 | TABLE ACCESS FULL | DUAL | 4072 | 11 | |
| 5 | TABLE ACCESS FULL | BBB | 41 | 287 | 2 |
| 6 | TABLE ACCESS BY INDEX ROWID | MEMBER | 1 | 54 | 2 |
| 7 | INDEX UNIQUE SCAN | MEMBER_SITE_LID_PK | 4 | 1 |
將條件改為 member.site(+) = 'test' 後,外連接恢復正常,驅動表回到 uidTable,查詢瞬間完成。
LEFT JOIN ... ON)可避免此類陷阱,建議新代碼採用標準寫法。(+)。LEFT JOIN 語法,將過濾條件移至 ON 子句中。