下面是自己寫的一個小樣,只是說說原理。
4 原始語句及執行計劃
由於在WHERE子句中使用了CASE ,使過濾謂詞使用的列出現了不確定性。
改寫後拆開WHERE字句中的CASE表達式,雖然執行計劃變得稍微複雜,但是COST大幅降低。
真實SQL比這個要複雜些,但是處理方法相同。
1建表
SQL> create table test_case_anti
2 as
3 select sysdate+1/24/60 d1, sysdate-356+1/24/60 d2, 1 d3
4 from dual
5 where 1=2 ;
Table created.
2 插入一百萬行數據
SQL> begin
2 for i in 1..1000000
3 loop
4 insert into test_case_anti
select sysdate+i/24/60 ,sysdate-356+i/24/60,i from dual ;
5 6 if mod(i,1000)=0 then commit ;end if ;
7
8 end loop ;
9
10 end ;
11 /
PL/SQL procedure successfully completed.
SQL> commit ;
Commit complete.
3 加索引
SQL> create index idx_dao_1 on TEST_CASE_ANTI(d1) ;
Index created.
SQL> create index idx_dao_2 on TEST_CASE_ANTI(d2) ;
Index created.
4 原始語句及執行計劃
SQL> select *
2 from test_case_anti x
3 where (case when d3=2 then x.d1 else d2 end )= trunc(sysdate+1) ;
no rows selected
Execution Plan
----------------------------------------------------------
Plan hash value: 875620970
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 40 | 1240 | 1036 (5)| 00:00:13 |
|* <span style="color:#FF0000;"> 1 | TABLE ACCESS FULL| TEST_CASE_ANTI | 40 | 1240 | 1036 (5)| 00:00:13 |</span>
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(CASE "D3" WHEN 2 THEN "X"."D1" ELSE "D2" END
=TRUNC(SYSDATE@!+1))
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
5 recursive calls
2 db block gets
3763 consistent gets
0 physical reads
0 redo size
458 bytes sent via SQL*Net to client
512 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
由於在WHERE子句中使用了CASE ,使過濾謂詞使用的列出現了不確定性。
ORACLE優化器無法判斷應該使用那個索引,所以給出全表的掃描執行計劃
5 對於此種情況,需要進行SQL改寫
> SQL> select *
from test_case_anti x
2 3 where d3=2
and d1=trunc(sysdate+1)
union all
select *
from test_case_anti x
where d3!=2
and d2=trunc(sysdate+1);
4 5 6 7 8 9
no rows selected
Execution Plan
----------------------------------------------------------
Plan hash value: 3938109978
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 121 | 3751 | 8 (50)| 00:00:01 |
| 1 | UNION-ALL | | | | | |
|* 2 | TABLE ACCESS BY INDEX ROWID| TEST_CASE_ANTI | 81 | 2511 | 4 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_DAO_1 | 40 | | 3 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| TEST_CASE_ANTI | 40 | 1240 | 4 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IDX_DAO_2 | 40 | | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("D3"=2)
3 - access("D1"=TRUNC(SYSDATE@!+1))
4 - filter("D3"<>2)
5 - access("D2"=TRUNC(SYSDATE@!+1))
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
9 recursive calls
2 db block gets
238 consistent gets
7 physical reads
0 redo size
458 bytes sent via SQL*Net to client
512 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
改寫後拆開WHERE字句中的CASE表達式,雖然執行計劃變得稍微複雜,但是COST大幅降低。