SQL最佳化案例-改變那些CBO無能為力的執行計劃(一)
使用者寫的sql,Oracle會進行等價改寫,即使是RBO最佳化模式,Oracle也會給你做一些轉換,這些轉化都是基於一種固定的演算法,oracle稱這種轉換是“啟發式”的。比如我們寫inner join時,並且只訪問單表資料,Oracle會自動降為半連線,然後用semi join的方式給你做join。transformation是Oracle必做的一個步驟,至少在8.05版本之後transformation都一直存在。
網上有很多最佳化法則,有的說exists比in效率高,有的說in比exists執行的快,那就要看SQL是如何寫的,CBO是如何轉換的,是否能轉換?當然這種轉換不是基於成本的而是“基於啟發的轉化”。
當Oracle沒辦法做transformation的時候,可能就是sql產生問題的時候,此時就要我們去找原因了,下面透過一些案例,說明這種最佳化器無能為力的情況(為了保護客戶的隱私,表名和部分列已經重新命名)。
| 用merge代替update
UPDATE關聯更新跑了將近40分鐘,SQL語句如下:
點選(此處)摺疊或開啟
- UPDATE PRO_S_ACCT A SET ACCT_SKID = (SELECT ACCT_SKID FROM ACCT_S_BK B WHERE A.ACCT_ID = B.ACCT_ID);
檢視量表資料量,其中PRO_S_ACCT有1044227行資料,acct_s_bk有553554行資料。
UPDATE後面跟子查詢類似巢狀迴圈。pro_s_acct為巢狀迴圈的驅動表,acct_s_bk為被驅動表,那麼表acct_s_bk就會被掃描100多萬次,就會產生大量的邏輯讀,被驅動表走全表掃描,我們可以在其上面建立索引,但是此時索引會被掃描100多萬次。
下面我們建立索引看其執行計劃如下:
點選(此處)摺疊或開啟
- create index ind_id_skid on acct_s_bk (ACCT_ID,ACCT_SKID);
下面我們透過用merge into 等價改寫看其執行計劃:
點選(此處)摺疊或開啟
-
merge into PRO_S_ACCT A
-
using ACCT_S_BK B on (A.ACCT_ID = B.ACCT_ID)
-
when matched
-
then update
- set a.ACCT_SKID = B.ACCT_SKID;
MERGE INTO可以自由控制走巢狀迴圈或者走hash連線,並且當驅動表和被驅動表的使用資料超過1G時我們可以開啟相應大小的並行DML更新。
點選(此處)摺疊或開啟
-
merge /*+PARALLEL(8 )*/ into PRO_S_ACCT A
-
using ACCT_S_BK B on (A.ACCT_ID = B.ACCT_ID)
-
when matched
-
then update
- set a.ACCT_SKID = B.ACCT_SKID;
實際執行中,2s完成。
下面透過sql改寫,來讓sql的執行計劃被我們所控制。
點選(此處)摺疊或開啟
-
UPDATE INXX I
-
SET (I.INT_FRM_DT,I.INT_TO_DT,I.ACCT_DESC) = (SELECT DBPP.CR_SOP_DATE,DBPP.EOP_DATE,DBPP.ACCT_DESC
-
FROM DBPP
-
WHERE DBPP.SYS_ID='INV'
-
AND DBPP.ACCT_TYPE = I.ACCT_TYPE
-
AND DBPP.INT_CAT = I.INT_CAT)
-
WHERE I.EXTDATE = TO_DATE('2018-04-03','YYYY-MM-DD')
-
AND EXISTS (SELECT DBPP.SYS_ID
-
FROM DBPP
-
WHERE DBPP.SYS_ID='INV'
-
AND DBPP.ACCT_TYPE = I.ACCT_TYPE
-
AND DBPP.INT_CAT = I.INT_CAT
- AND DBPP.ACCT_DESC = 'S');
點選(此處)摺疊或開啟
-
merge /*+parallel(10) use_hash(I,X) swap_join_inputs(X)*/ into INXX I
-
using (SELECT DBPP.CR_SOP_DATE,DBPP.EOP_DATE,DBPP.ACCT_DESC,DBPP.ACCT_TYPE,DBPP.INT_CAT FROM DBPP WHERE DBPP.SYS_ID='INV' AND DBPP.ACCT_DESC = 'S') x
-
on (x.ACCT_TYPE = I.ACCT_TYPE AND x.INT_CAT = I.INT_CAT)
-
when matched
-
then update set I.INT_FRM_DT=x.CR_SOP_DATE,I.INT_TO_DT=x.EOP_DATE,I.ACCT_DESC=x.ACCT_DESC
- WHERE I.EXTDATE = TO_DATE('2018-04-03','YYYY-MM-DD');
另一類似案例:
點選(此處)摺疊或開啟
-
update WWW a
-
set a.cny_bal=a.ll_bal*nvl((select b.hl from MMM b where b.startdate<=a.extedate and b.enddate > a.extdate and b.zb='CNY' and
-
a.curr=b.yb),0)
-
where a.extdate=to_date('2018-04-01','yyyy-mm-dd');
-
由於www表是按天分割槽,分割槽欄位是extdate,那麼可以起改寫成如下:
-
merge /*+parallel(8)*/ into www a
-
using (select b.hl from MMM b where b.zb='CNY' and b.enddate>date'2018-04-01' and b.startdate<=date'2018-04-01') c
-
on (a.curr=c.yb)
-
when matched
-
then update
-
set a.cny_bal=a.ll_bal*NVL(c.hl,0)
- where a.extdate=to_date('2018-04-01','yyyy-mm-dd');
| 有關外連結的其他改寫
點選(此處)摺疊或開啟
-
SELECT
-
CASE WHEN
-
NOT EXISTS (SELECT KHH FROM NB_XXXXXXXX B WHERE RQ>=ADD_MONTHS(TO_DATE('2018-04-27','YYYY-MM-DD'),-12) AND RQ<=TO_DATE('2018-04-27','YYYY-MM-DD') AND A.CUSTNO=B.KHH)
-
AND A.OPENCUPDATE+365=TO_DATE('2018-04-27','YYYY-MM-DD') THEN A.CUSTNO END BQXZ,
-
CASE THEN
-
NOT EXISTS (SELECT KHH FROM NB_XXXXXXXX B WHERE RQ>=ADD_MONTHS(TO_DATE('2018-04-27','YYYY-MM-DD'),-12) AND RQ<=TO_DATE('2018-04-27','YYYY-MM-DD') AND A.CUSTNO=B.KHH)
-
AND A.OPENCUPDATE+365=TO_DATE('2018-04-27','YYYY-MM-DD') THEN A.CUSTNO END YE,
-
'2' AS QD,
-
SUBSTR(B.OPENBANKNO,1,4) JGM
-
FROM NB_CCCCCCCCC A
-
inner join DZZH_XXXXXXXXXXXXXXXXXX B
-
ON A.CUSTNO = B.CUSTNO
-
WHERE CUPCHECKSTT IN ('1','2');
- 685012 rows selected
由於環境是跑批業務,建立索引需要全面考慮,為了不改變當前環境我們儘量不建立索引,執行計劃如下:
實際執行時間37分鐘完成。
點選(此處)摺疊或開啟
-
SELECT
-
CASE WHEN c.khh is null then A.CUSTNO END BQXZ,
-
CASE WHEN c.khh is null then A.CUSTNO END ye,
-
'2' AS QD,
-
SUBSTR(B.OPENBANKNO,1,4) JGM
-
from NB_CCCCCCCCC A
-
inner join DZZH_XXXXXXXXXXXXXXXXXX B
-
ON A.CUSTNO = B.CUSTNO
-
left join
-
(SELECT KHH FROM NB_XXXXXXXX B WHERE RQ>=ADD_MONTHS(TO_DATE('2018-04-27','YYYY-MM-DD'),-12) AND RQ<=TO_DATE('2018-04-27','YYYY-MM-DD')) c
-
on A.CUSTNO=c.KHH and A.OPENCUPDATE+365=TO_DATE('2018-04-27','YYYY-MM-DD')
-
where CUPCHECKSTT IN ('1','2');
- 685012 rows selected
| 作者簡介
姚崇·沃趣科技高階資料庫技術專家
熟悉Oracle資料庫內部機制,豐富的資料庫及RAC叢集層故障診斷、效能調優、OWI、資料庫備份恢復及遷移經驗。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28218939/viewspace-2154052/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- SQL優化案例-改變那些CBO無能為力的執行計劃(一)SQL優化
- Oracle優化案例-改變那些CBO無能為力的執行計劃(一)Oracle優化
- sql執行計劃是否改變SQL
- 11g 改變SQL執行計劃SQL
- SQL 執行計劃案例1SQL
- 利用coe_xfr_sql_profile 改變執行計劃SQL
- SQL最佳化案例-從執行計劃定位SQL問題(三)SQL
- 使用hint改變執行計劃
- 約束Constraint引起CBO執行計劃變化一例AI
- 使用rownum改變執行計劃的一個典型情況
- SQL最佳化 —— 讀懂執行計劃SQL
- 加hint改變執行計劃訪問順序優化sql優化SQL
- 一條SQL語句的執行計劃變化探究SQL
- sql執行計劃變更和刪除快取中執行計劃的方法SQL快取
- Oracle 通過註釋改變執行計劃Oracle
- 分析執行計劃最佳化SQLORACLE的執行計劃(轉)SQLOracle
- SQL的執行計劃SQL
- sql 執行計劃SQL
- 檢視sql 執行計劃的歷史變更SQL
- SQL優化案例-從執行計劃定位SQL問題(三)SQL優化
- 11g改變了DELETE語句的執行計劃delete
- 增加索引改變執行計劃——SQL優化之Everything is possible索引SQL優化
- SQLServer統計監控SQL執行計劃突變的方法SQLServer
- spark sql語句效能最佳化及執行計劃SparkSQL
- 透過分析SQL語句的執行計劃最佳化SQL(總結)SQL
- 控制執行計劃之-SQL Profile(一)SQL
- Oracle sql執行計劃OracleSQL
- SQL Server執行計劃SQLServer
- SQL執行計劃分析SQL
- Calcite執行計劃最佳化
- sql最佳化:使用儲存提綱穩定sql執行計劃SQL
- 【sql調優之執行計劃】獲取執行計劃SQL
- sql的執行計劃 詳解SQL
- [轉]透過分析SQL語句的執行計劃最佳化SQL(總結)SQL
- 微課sql最佳化(9)、如何獲取執行計劃SQL
- 微課sql最佳化(11) 、如何檢視執行計劃SQL
- 另我無語啊,SQL執行計劃走錯SQL
- 【Oracle】如何檢視sql 執行計劃的歷史變更OracleSQL