使用反CASE思想,改寫SQL

下面是自己寫的一個小樣,只是說說原理。

真實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大幅降低。



發佈了91 篇原創文章 · 獲贊 8 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章