使用反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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章