oracle sql 高階程式設計學習筆記(二十四)
阿新 • • 發佈:2018-11-21
在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,而且時間也是內聯的一半。可見子查詢因子話即將
結果集寫入到臨時表中的效率在多次重用的效率明顯提高。