SQL優化【基礎09】 - 查詢轉換(query transformation)

--version:11.2
前言:爲什麼要查詢轉換呢?
簡單的說就是ORACLE在等邏輯的基礎上增加可能存在的執行計劃,有更多的選擇,從中選出最優的計劃;

常見的3種轉換
1.視圖合併
2.子查詢展開
3.謂詞推入


create view v_t1t2 as 
select  t1.object_id,t2.status
from t1,t2
where t1.object_id=t2.object_id;

SQL> create view v_t1t2 as 
  2  select  t1.object_id,t2.status
  3  from t1,t2
  4  where t1.object_id=t2.object_id;

View created.

SQL> create table t1 as select * from dba_objects;

Table created.

SQL> create table t2 as select * from dba_objects where rownum<1001;

Table created.

SQL> create table t3 as select * from dba_objects where rownum<11;

Table created.

一. 視圖合併

--如下執行SQL,視圖v_t1t2沒有出現在name列
select  t3.object_id,vv.status
from t3,v_t1t2  vv
where t3.object_id=vv.object_id;

SQL_ID  9w0yg7pp6kph3, child number 0
-------------------------------------
 select  t3.object_id,vv.status from t3,v_t1t2  vv where
t3.object_id=vv.object_id

Plan hash value: 2467348796

-----------------------------------------------------------------------------------------------------------------
| Id  | Operation           | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |      1 |        |      9 |00:00:02.03 |    1060 |       |       |          |
|*  1 |  HASH JOIN          |      |      1 |      1 |      9 |00:00:02.03 |    1060 |  1306K|  1306K| 1128K (0)|
|*  2 |   HASH JOIN         |      |      1 |     10 |     10 |00:00:02.01 |    1043 |  1517K|  1517K| 1123K (0)|
|   3 |    TABLE ACCESS FULL| T3   |      1 |     10 |     10 |00:00:00.01 |       3 |       |       |          |
|   4 |    TABLE ACCESS FULL| T1   |      1 |  62548 |  72668 |00:00:00.55 |    1040 |       |       |          |
|   5 |   TABLE ACCESS FULL | T2   |      1 |   1000 |   1000 |00:00:00.01 |      17 |       |       |          |
-----------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("T1"."OBJECT_ID"="T2"."OBJECT_ID")
   2 - access("T3"."OBJECT_ID"="T1"."OBJECT_ID")

Note
-----
   - dynamic sampling used for this statement (level=2)
   
--加入no_merge的hint可以name列看到v_t1t2的視圖名稱   
 select  /*+ no_merge(vv) */t3.object_id,vv.status from t3,v_t1t2  vv where
t3.object_id=vv.object_id;   

SQL_ID  4bkn59yaq6chu, child number 0
-------------------------------------
      select  /*+ no_merge(vv) */t3.object_id,vv.status from t3,v_t1t2
vv where t3.object_id=vv.object_id

Plan hash value: 1147613535

-------------------------------------------------------------------------------
| Id  | Operation            | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |        |       |       |   300 (100)|          |
|*  1 |  HASH JOIN           |        |    10 |   310 |   300   (1)| 00:00:04 |
|   2 |   TABLE ACCESS FULL  | T3     |    10 |   130 |     3   (0)| 00:00:01 |
|   3 |   VIEW               | V_T1T2 |   999 | 17982 |   297   (1)| 00:00:04 |
|*  4 |    HASH JOIN         |        |   999 | 30969 |   297   (1)| 00:00:04 |
|   5 |     TABLE ACCESS FULL| T2     |  1000 | 18000 |     6   (0)| 00:00:01 |
|   6 |     TABLE ACCESS FULL| T1     | 62548 |   794K|   290   (1)| 00:00:04 |
-------------------------------------------------------------------------------


二.子查詢展開

--如下SQL的子查詢中的T3與外邊的T1形成HASH JOIN RIGHT SEMI(HASH半連接)
select  t1.status,t2.object_name
from t1,t2
where t1.object_id=t2.object_id
and t1.object_id in (select object_id from t3 );

select  t1.status,t2.object_name from t1,t2 where
t1.object_id=t2.object_id and t1.object_id in (select object_id from t3
)

Plan hash value: 2897334926

------------------------------------------------------------------------------
| Id  | Operation             | Name | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |      |       |       |   301 (100)|          |
|*  1 |  HASH JOIN            |      |     1 |   110 |   301   (1)| 00:00:04 |
|*  2 |   HASH JOIN RIGHT SEMI|      |    10 |   310 |   294   (1)| 00:00:04 |
|   3 |    TABLE ACCESS FULL  | T3   |    10 |   130 |     3   (0)| 00:00:01 |
|   4 |    TABLE ACCESS FULL  | T1   | 62548 |  1099K|   290   (1)| 00:00:04 |
|   5 |   TABLE ACCESS FULL   | T2   |  1000 | 79000 |     6   (0)| 00:00:01 |
------------------------------------------------------------------------------


--加入no_unnest的hint可以看到不展開將會以filter的連接方式來執行子查詢SQL
select  t1.status,t2.object_name
from t1,t2
where t1.object_id=t2.object_id
and t1.object_id in (select /*+ no_unnest */object_id from t3 );

select  t1.status,t2.object_name from t1,t2 where
t1.object_id=t2.object_id and t1.object_id in (select /*+ no_unnest
*/object_id from t3 )

Plan hash value: 2685818224

----------------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |       |       |  1796 (100)|          |
|*  1 |  FILTER             |      |       |       |            |          |
|*  2 |   HASH JOIN         |      |   999 | 96903 |   297   (1)| 00:00:04 |
|   3 |    TABLE ACCESS FULL| T2   |  1000 | 79000 |     6   (0)| 00:00:01 |
|   4 |    TABLE ACCESS FULL| T1   | 62548 |  1099K|   290   (1)| 00:00:04 |
|*  5 |   TABLE ACCESS FULL | T3   |     1 |    13 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------

三.謂詞推入


SQL> create index idx_t1_id on t1(object_id);

Index created.

SQL> create index idx_t2_id on t2(object_id);

Index created.

SQL> create index idx_t3_id on t3(object_id);

Index created.


select /*+  push_pred(vv) no_merge(vv)  */ t3.status,vv.object_id from
t3,v_t1t2 vv where t3.object_id=vv.object_id(+) and t3.object_id=99

Plan hash value: 3425305862

------------------------------------------------------------------------------------------
| Id  | Operation                    | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |           |       |       |     3 (100)|          |
|   1 |  NESTED LOOPS OUTER          |           |     1 |    31 |     3   (0)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID| T3        |     1 |    18 |     1   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | IDX_T3_ID |     1 |       |     1   (0)| 00:00:01 |
|   4 |   VIEW PUSHED PREDICATE      | V_T1T2    |     1 |    13 |     2   (0)| 00:00:01 |     --謂詞推入關鍵字
|*  5 |    FILTER                    |           |       |       |            |          |
|   6 |     MERGE JOIN CARTESIAN     |           |     1 |    26 |     2   (0)| 00:00:01 |
|*  7 |      INDEX RANGE SCAN        | IDX_T1_ID |     1 |    13 |     1   (0)| 00:00:01 |
|   8 |      BUFFER SORT             |           |     1 |    13 |     1   (0)| 00:00:01 |
|*  9 |       INDEX RANGE SCAN       | IDX_T2_ID |     1 |    13 |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------------



四.查詢轉換與應用
--那麼查詢轉換與優化有什麼關係?爲什麼要去了解?
看個例子,我們將上面的其中一條關於謂詞推入的SQL改寫下:
--不讓它進行謂詞推入
select /*+  no_push_pred(vv) merge(vv)  */ t3.status,vv.object_id from
t3,v_t1t2 vv where t3.object_id=vv.object_id(+) and t3.object_id=99

select /*+  no_push_pred(vv) merge(vv)  */ t3.status,vv.object_id from
t3,v_t1t2 vv where t3.object_id=vv.object_id(+) and t3.object_id=99

Plan hash value: 2569520452

------------------------------------------------------------------------------------------
| Id  | Operation                    | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |           |       |       |     4 (100)|          |
|*  1 |  HASH JOIN OUTER             |           |     1 |    31 |     4  (25)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID| T3        |     1 |    18 |     1   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | IDX_T3_ID |     1 |       |     1   (0)| 00:00:01 |
|   4 |   VIEW                       | V_T1T2    |     1 |    13 |     2   (0)| 00:00:01 |
|   5 |    MERGE JOIN CARTESIAN      |           |     1 |    26 |     2   (0)| 00:00:01 |
|*  6 |     INDEX RANGE SCAN         | IDX_T1_ID |     1 |    13 |     1   (0)| 00:00:01 |
|   7 |     BUFFER SORT              |           |     1 |    13 |     1   (0)| 00:00:01 |
|*  8 |      INDEX RANGE SCAN        | IDX_T2_ID |     1 |    13 |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------------

--然後去除HINT默認讓它運行
select  t3.status,vv.object_id from
t3,v_t1t2 vv where t3.object_id=vv.object_id(+) and t3.object_id=99;

SQL_ID  f3x5z80ammrb2, child number 0
-------------------------------------
select  t3.status,vv.object_id from t3,v_t1t2 vv where
t3.object_id=vv.object_id(+) and t3.object_id=99

Plan hash value: 3425305862

------------------------------------------------------------------------------------------
| Id  | Operation                    | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |           |       |       |     3 (100)|          |
|   1 |  NESTED LOOPS OUTER          |           |     1 |    31 |     3   (0)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID| T3        |     1 |    18 |     1   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | IDX_T3_ID |     1 |       |     1   (0)| 00:00:01 |
|   4 |   VIEW PUSHED PREDICATE      | V_T1T2    |     1 |    13 |     2   (0)| 00:00:01 |
|*  5 |    FILTER                    |           |       |       |            |          |
|   6 |     MERGE JOIN CARTESIAN     |           |     1 |    26 |     2   (0)| 00:00:01 |
|*  7 |      INDEX RANGE SCAN        | IDX_T1_ID |     1 |    13 |     1   (0)| 00:00:01 |
|   8 |      BUFFER SORT             |           |     1 |    13 |     1   (0)| 00:00:01 |
|*  9 |       INDEX RANGE SCAN       | IDX_T2_ID |     1 |    13 |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------------

--可以看到推入後的COST值,ORACLE計算的結果爲3比上一條推入的4略小;
在日常的運維中如果發現SQL在推入後的效果更佳時你就可以考慮推入這個方法去暫時解決這個問題,以及要清楚
哪些條件可讓ORACLE進入推入的操作,哪些不能; 
如下舉例,不同類型的視圖,相同的連接類型,一個可以推入,一個不行;
        create view v_tt as 
  2    select t2.object_id,t1.status 
  3    from t1,t2
  4    where t1.object_id=t2.object_id
  5    union 
  6    select t1.object_id,t3.status
  7    from t1,t3
  8    where t1.object_id=t3.object_id
  9    ;

SQL_ID  dttpqvxhpjwm4, child number 0
-------------------------------------
   select  /*+ push_pred(v_tt) */t1.status,v_tt.object_id    from v_tt
,t1     where v_tt.object_id=t1.object_id(+)     and t1.object_id=88

Plan hash value: 1281273642

------------------------------------------------------------------------------------------------
| Id  | Operation                          | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                   |           |       |       |     9 (100)|          |
|   1 |  NESTED LOOPS                      |           |     2 |    40 |     9  (23)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID      | T1        |     1 |    18 |     2   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                | IDX_T1_ID |     1 |       |     1   (0)| 00:00:01 |
|   4 |   VIEW                             | V_TT      |     2 |     4 |     7  (29)| 00:00:01 |
|   5 |    SORT UNIQUE                     |           |     2 |    62 |     7  (58)| 00:00:01 |
|   6 |     UNION ALL PUSHED PREDICATE     |           |       |       |            |          |     --推入
|*  7 |      FILTER                        |           |       |       |            |          |
|   8 |       MERGE JOIN CARTESIAN         |           |     1 |    31 |     3   (0)| 00:00:01 |
|   9 |        TABLE ACCESS BY INDEX ROWID | T1        |     1 |    18 |     2   (0)| 00:00:01 |
|* 10 |         INDEX RANGE SCAN           | IDX_T1_ID |     1 |       |     1   (0)| 00:00:01 |
|  11 |        BUFFER SORT                 |           |     1 |    13 |     1   (0)| 00:00:01 |
|* 12 |         INDEX RANGE SCAN           | IDX_T2_ID |     1 |    13 |     1   (0)| 00:00:01 |
|* 13 |      FILTER                        |           |       |       |            |          |
|  14 |       MERGE JOIN CARTESIAN         |           |     1 |    31 |     2   (0)| 00:00:01 |
|* 15 |        INDEX RANGE SCAN            | IDX_T1_ID |     1 |    13 |     1   (0)| 00:00:01 |
|  16 |        BUFFER SORT                 |           |     1 |    18 |     1   (0)| 00:00:01 |
|  17 |         TABLE ACCESS BY INDEX ROWID| T3        |     1 |    18 |     1   (0)| 00:00:01 |
|* 18 |          INDEX RANGE SCAN          | IDX_T3_ID |     1 |       |     0   (0)|          |
------------------------------------------------------------------------------------------------


--把v_t1t2代替原來v_tt視圖,設置了no_merge,push_pred依然不能推入,視圖類型不一樣;
SQL_ID  7wq68281uxuah, child number 0
-------------------------------------
       select  /*+ no_merge(v_tt)  push_pred(v_tt)
*/t1.status,v_tt.object_id    from v_t1t2 v_tt ,t1     where
v_tt.object_id=t1.object_id(+)     and t1.object_id=88

Plan hash value: 1712600844

-------------------------------------------------------------------------------------------
| Id  | Operation                     | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |           |       |       |     4 (100)|          |
|   1 |  MERGE JOIN CARTESIAN         |           |     1 |    31 |     4   (0)| 00:00:01 |
|   2 |   VIEW                        | V_T1T2    |     1 |    13 |     2   (0)| 00:00:01 |
|   3 |    MERGE JOIN CARTESIAN       |           |     1 |    26 |     2   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN          | IDX_T1_ID |     1 |    13 |     1   (0)| 00:00:01 |
|   5 |     BUFFER SORT               |           |     1 |    13 |     1   (0)| 00:00:01 |
|*  6 |      INDEX RANGE SCAN         | IDX_T2_ID |     1 |    13 |     1   (0)| 00:00:01 |
|   7 |   BUFFER SORT                 |           |     1 |    18 |     4   (0)| 00:00:01 |
|   8 |    TABLE ACCESS BY INDEX ROWID| T1        |     1 |    18 |     2   (0)| 00:00:01 |
|*  9 |     INDEX RANGE SCAN          | IDX_T1_ID |     1 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------


其它的查詢轉換類型還包含有:
連接因式分解,表擴展(table expansion),表移除,in的改寫形式 


--連接因式分解(VW_JF_XX),JF:join factorization,關鍵提示字眼
概念:針對union all語句,對其中的公共部份拿出再與原SQL的剩餘部份做連接
條件:須等價語義轉換這個大前提


--表擴展(table expansion)
概念:其中一個分區的索引失效並不影響其它分區的索引使用情況,11G R2前一個分區索引失效,走索引會全部轉爲全表掃描;
前提:基於成本考慮
expand_table(t1)/no_expand_table(t1)


--表移除
概念:把不影響SQL執行結果的表刪除,少做一次連接

備註:lnnvl函數lnnvl(deptno in (10) )

--IN寫法
(1)第一種
in-list iterator
可被10142,10157事件禁止

(2)第二種
in-list iterator expansion
概念:將in後面的變量用union all來連接各個分支,保證每個分去的計劃正確性,缺點分支越長越耗解析時長
,並且當in-list iterator生效時,使用use_concat(此HINT)並不生效,可用10142,10157將interator禁掉

(3)第三種
in做爲filter的過濾條件
滿足兩條件a.in後面爲子查詢而非常量 b. in後面的子查詢不可展開

(4)第四種
子查詢展開與(子查詢中包含視圖)的合併


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章