1. 程式人生 > >oracle sql 高階程式設計學習筆記(二十四)

oracle sql 高階程式設計學習筆記(二十四)

在oracle 官方文件中有如下一段話:
注意,oracle 可能將因子化的子查詢作為臨時表來處理,在一個表被多次引用的查詢中,這可能是獨特
的效能優勢,因為oracle可以物化查詢結果集 從而避免多次執行一些非常耗費
資源的資料庫運算。在這裡需要注意的是,這種優化只是可能的獨特效能優勢。需要牢記於心的一點是物化結果集需要建立
一個臨時表並將資料行插入其中。如果同一個結果集會被多次引用,這樣做可能是很值得的,否則就有可能極大的降低效能。
–用materialize提示將查詢因子物化成臨時表。(不加提示時,本例也會預設採用這種辦法)

子查詢因子化例項演示

--測試前 先在dba許可權清除快取
ALTER SYSTEM FLUSH SHARED_POOL;
ALTER SYSTEM FLUSH BUFFER_CACHE;
ALTER SYSTEM FLUSH GLOBAL CONTEXT;

1、使用子查詢因子化

with cust as
 (select /*+materialize gather_plan_statistics*/
   b.cust_income_level, a.country_name
    from customers b
    join countries a
      on a.country_id = b.country_id)
select country_name,
       cust_income_level,
       count(country_name) country_cust_count
  from cust
having count (country_name) > (select count(1) * 0.01 from cust c2) 
or count (cust_income_level) >= 
--median  用於計算中位數
(select median(income_level_count) from (
select cust_income_level,count(1) * 0.25 income_level_count from cust
   group by cust_income_level))
group by  country_name,
       cust_income_level
       order by 1,2;
       --根據第一列和第二列排序 
既相當於 order by  country_name,  cust_income_level

執行後
獲取sqlid

select  t.SQL_TEXT,t.SQL_ID,t.CHILD_NUMBER from  v$sql t  where t.SQL_TEXT like '%materialize%';  

在這裡插入圖片描述

檢視執行計劃:

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('f26yd87mjkfwg',0,'ALLSTATS LAST'));  

結果如下:
在這裡插入圖片描述

可以看到customers以及countries表的聯結經過了一個臨時錶轉化( TEMP TABLE TRANSFORMATION),接下來的查詢都會用到這個臨時表SYS_TEMP_0FD9D6609_2630CA,並且由於建立了臨時表,產生了物理讀取 reads 既IO

2、使用INLINE提示,查詢因子做內聯處理。

with cust as
 (select /*+ inline gather_plan_statistics*/ 
   b.cust_income_level, a.country_name
    from customers b
    join countries a
      on a.country_id = b.country_id)
select country_name,
       cust_income_level,
       count(country_name) country_cust_count
  from cust
having count (country_name) > (select count(1) * 0.01 from cust c2) 
or count (cust_income_level) >= 
(select median(income_level_count) from (
select cust_income_level,count(1) * 0.25 income_level_count from cust
   group by cust_income_level))
group by  country_name,
       cust_income_level
       order by 1,2;

執行完後檢視 sql_id

select  t.SQL_TEXT,t.SQL_ID,t.CHILD_NUMBER from  v$sql t  where t.SQL_TEXT like '%inline%';
執行計劃 

在這裡插入圖片描述

可以看到對customers表進行了三次全表掃描以及countries進行了一次全表掃描對cust子查詢進行了兩次索引全掃描
同時由於全表掃描也產生了物理讀取,有意思的是相對物化既第一個sql中執行計劃,少了300多個IO。而時間也是比 第一個提高了一半;

3、再增加一次結果集的引用

在檢視執行計劃時,發現需要執行兩次sql,不然得不到執行計劃 會有如下提示 未找到原因
在這裡插入圖片描述

with cust as

 (select /*+materialize gather_plan_statistics*/
   b.cust_income_level, a.country_name
    from customers b
    join countries a
      on a.country_id = b.country_id),
      median_income_set as  
     (select /*+materialize*/ cust_income_level,count(1) income_level_count  from cust
      group by cust_income_level 
      having count(cust_income_level)>(
      select  median(income_level_count) income_level_count from   (
      select  cust_income_level,count(1) income_level_count from   cust group by cust_income_level
      )
      ))
select country_name,
       cust_income_level,
       count(country_name) country_cust_count
  from cust c
having count (country_name) > (select count(1) * 0.01 from cust c2) 
or cust_income_level in(select  mis.cust_income_level from   median_income_set mis)
group by  country_name,
       cust_income_level
       order by 1,2
;

執行計劃
在這裡插入圖片描述

4、增加一次結果集 使用內聯 *+inline


with cust as
 (select /*+inline gather_plan_statistics*/
   b.cust_income_level, a.country_name
    from customers b
    join countries a
      on a.country_id = b.country_id),
      median_income_set as  
     (select /*+inline*/ cust_income_level,count(1) income_level_count  from cust
      group by cust_income_level 
      having count(cust_income_level)>(
      select  median(income_level_count) income_level_count from   (
      select  cust_income_level,count(1) income_level_count from   cust group by cust_income_level
      )
      ))
select country_name,
       cust_income_level,
       count(country_name) country_cust_count
  from cust c
having count (country_name) > (select count(1) * 0.01 from cust c2) 
or cust_income_level in(select  mis.cust_income_level from   median_income_set mis)
group by  country_name,
       cust_income_level
       order by 1,2;

執行計劃
在這裡插入圖片描述

增加一次結果集的引用後發現,子查詢因子話 的邏輯讀取只有3000左右 比內聯 少了僅2w,而且時間也是內聯的一半。可見子查詢因子話即將
結果集寫入到臨時表中的效率在多次重用的效率明顯提高。