当 SQL 语句中同时有 or 和子查询,这种情况下子查询无法展开(unnest),只能走 FILTER。遇到这种情况我们可以将 SQL 改写为 union,从而消除 FILTER。
带有 or 子查询的写法与执行计划如下。
select *
from t1
where owner = 'SCOTT'
or object_id in (select object_id from t2);
72571 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 895956251
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3378 | 682K| 235 (1)| 00:00:03 |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS FULL| T1 | 56766 | 11M| 235 (1)| 00:00:03 |
|* 3 | TABLE ACCESS FULL| T2 | 734 | 9542 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("OWNER"='SCOTT' OR EXISTS (SELECT 0 FROM "T2" "T2" WHERE
"OBJECT_ID"=:B1))
3 - filter("OBJECT_ID"=:B1)
改写为 union 的写法如下。
select * from t1 where owner='SCOTT'
union
select * from t1 where object_id in(select object_id from t2);
72571 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 696035008
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)|
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 56778 | 11M| | 4088 (95)|
| 1 | SORT UNIQUE | | 56778 | 11M| 12M| 4088 (95)|
| 2 | UNION-ALL | | | | | |
|* 3 | TABLE ACCESS FULL | T1 | 12 | 2484 | | 234 (1)|
|* 4 | HASH JOIN | | 56766 | 11M| 1800K| 1146 (1)|
| 5 | TABLE ACCESS FULL| T2 | 73407 | 931K| | 234 (1)|
| 6 | TABLE ACCESS FULL| T1 | 56766 | 11M| | 235 (1)|
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("OWNER"='SCOTT')
4 - access("OBJECT_ID"="OBJECT_ID")
改写为 union 之后,消除了 FILTER。如果无法改写 SQL,那么 SQL 就只能走 FILTER,这时我们需要在子查询表的连接列(t2.object_id)建立索引。