SQL最佳化案例-從執行計劃定位SQL問題(三)
當SQL出現問題,能從執行計劃中快速的定位哪部分出現問題很重要,SQL文字如下(為保證客戶隱私,已經將註釋和文字部分去掉):
點選(此處)摺疊或開啟
-
SELECT /*+ index(i IDX_INVM_BEC)*/ RQ,JGM,BZ,CUSTOMER_TYPE,
-
B.CUSTOMER_NO,
-
B.CUSTOMER_NAME AS DKHM,
-
B.ACCT_NO DKZH,
-
B.STATUS,
-
B.LOAN_BAL,
-
P,
-
LX,
-
NVL((SELECT A.ACCT_NO_DESC || A.ACCT_NO_ALL2
-
FROM INVM_ZMQ A
-
WHERE A.ACCT_NO=I.ACCT_NO
-
AND A.ZHLB='3'),I.ACCT_NO) AS CKZH,
-
I.CURR_VAL,
-
(CASE WHEN B.TRANSFER_ACCT=I.ACCT_NO THEN '嘻嘻嘻' ELSE '' END) AS SM
-
FROM(
-
SELECT B.EXTDATE AS RQ,
-
B.BRANCH_NO AS JGM,
-
B.CURRENCY AS BZ,
-
C.CUSTOMER_TYPE,
-
B.CUSTOMER_NO,
-
C.CUSTOMER_NAME,
-
B.ACCT_NO,
-
'啊啊' AS STATUS,
-
B.LOAN_BAL,
-
B.UNPD_PRIN_BAL AS P,
-
ROUND(B.CAP_UNPD_INT,2)+
-
(CASE WHEN B.REPAY_SCHED IN ('M','G') OR L.REPAY_SCHED IN ('M','G') THEN 0
-
ELSE (CASE WHEN L.PIA_CAPN_FREQ='S' AND L.PAST_DUE_CAPN_FREQ='S'
-
THEN ROUND(B.THEO_UNPD_ARR_PRN,2)
-
+(CASE WHEN L.FINE1_COMPD_OPT='Y' THEN ROUND(B.THEO_UNPD_ARRS_INT,2) ELSE 0 END)
-
+(CASE WHEN L.FINE2_COMPD_OPT='Y' THEN ROUND(B.THEO_UNPD_INT_ARR,2) ELSE 0 END)
-
ELSE 0
-
END)
-
END) AS LX,
-
B.TRF_ACCT_NO AS TRANSFER_ACCT
-
FROM BORM PARTITION("BORM_2018-06-13") B
-
INNER JOIN LONP L ON L.ACCT_TYPE=B.ACCT_TYPE AND L.INT_CAT=B.INT_CAT
-
INNER JOIN JGDY J ON B.BRANCH_NO = J.JGM AND (J.JGM='1700' OR J.SJJGM='1700')
-
LEFT JOIN CB_ACCT C ON B.ACCT_NO = C.ACCT_NO AND C.SYS_ID = 'BOR'
-
WHERE B.BAD_DEBT_IND in ('02','52')
-
AND B.STAT<>'40'
-
) B
-
LEFT JOIN INVM PARTITION("INVM_2018-06-13") I
-
ON B.CUSTOMER_NO=I.CUSTOMER_NO
-
AND I.ACCT_DESC='S'
-
AND I.CURR_VAL<>0
-
AND I.BRANCH_NO IN (SELECT JGM FROM JGDY WHERE JGM='1700' OR SJJGM='1700')
-
AND I.EXTDATE = DATE'2018-06-13'
-
AND I.CURR_STATUS='00'
- WHERE (B.P>0 OR B.LX>0.01);
執行計劃如下:
可以一眼定位到view部分導致整個執行緩慢,那麼我們仔細分析下view部分是怎麼執行的。INVM TABLE ACCESS BY LOCAL INDEX ROWID執行11分鐘,總計13分鐘執行完。
尋找view部分執行計劃的入口,ID18和ID19做nested loop,返回結果17與21做NESTED LOOP,可以得知最先執行的是ID18,ID18走的iffs,且A-rows返回記錄6256行資料,檢視ID18謂詞資訊
18 - filter(("SJJGM"='1700' OR "JGM"='1700')) 從這部分再回到SQL文字尋找SQL程式碼是AND I.BRANCH_NO IN (SELECT JGM FROM JGDY WHERE JGM='1700' OR SJJGM='1700')也就是【SELECT JGM FROM JGDY WHERE JGM='1700' OR SJJGM='1700'】,難道這部分返回結果真的是6256行資料嗎?帶著疑問我查詢了一下。
居然只返回了34行資料,為什麼會這樣子?
肯定是此處的JGDY_IDX3有什麼問題,那麼會有什麼問題呢?往上看ID為8的JGDY_IDX3正確的返回了34行資料,又仔細看了下ID為18的JGDY_IDX3,starts184次,正好6256/184=34,那麼原因找到了,正是因為ID4和ID15做NESTED LOOP,導致檢視裡面所有的部分都要多執行184次。按照上面的分析思路看ID4裡面的執行計劃都很正確,但是返回結果184行記錄且ID4和ID15做nested loop,導致整個view部分緩慢。
那麼就很好辦了,ID4和ID15應該走hash join,檢視outline data資訊,還沒辦法使用db_name資訊引導執行計劃走hash join,那麼只能改寫SQL。
改寫SQL如下:
點選(此處)摺疊或開啟
-
SELECT RQ,JGM,BZ,CUSTOMER_TYPE,
-
B.CUSTOMER_NO,
-
B.CUSTOMER_NAME AS DKHM,
-
B.ACCT_NO DKZH,
-
B.STATUS,
-
B.LOAN_BAL,
-
P,
-
LX,
-
NVL((SELECT A.ACCT_NO_DESC || A.ACCT_NO_ALL2
-
FROM INVM_ZMQ A
-
WHERE A.ACCT_NO=C.ACCT_NO
-
AND A.ZHLB='3'),C.ACCT_NO) AS CKZH,
-
C.CURR_VAL,
-
(CASE WHEN B.TRANSFER_ACCT=C.ACCT_NO THEN '嘻嘻嘻' ELSE '' END) AS SM
-
FROM(
-
SELECT B.EXTDATE AS RQ,
-
B.BRANCH_NO AS JGM,
-
B.CURRENCY AS BZ,
-
C.CUSTOMER_TYPE,
-
B.CUSTOMER_NO,
-
C.CUSTOMER_NAME,
-
B.ACCT_NO,
-
'啊啊' AS STATUS,
-
B.LOAN_BAL,
-
B.UNPD_PRIN_BAL AS P,
-
ROUND(B.CAP_UNPD_INT,2)+
-
(CASE WHEN B.REPAY_SCHED IN ('M','G') OR L.REPAY_SCHED IN ('M','G') THEN 0
-
ELSE (CASE WHEN L.PIA_CAPN_FREQ='S' AND L.PAST_DUE_CAPN_FREQ='S'
-
THEN ROUND(B.THEO_UNPD_ARR_PRN,2)
-
+(CASE WHEN L.FINE1_COMPD_OPT='Y' THEN ROUND(B.THEO_UNPD_ARRS_INT,2) ELSE 0 END)
-
+(CASE WHEN L.FINE2_COMPD_OPT='Y' THEN ROUND(B.THEO_UNPD_INT_ARR,2) ELSE 0 END)
-
ELSE 0
-
END)
-
END) AS LX,
-
B.TRF_ACCT_NO AS TRANSFER_ACCT
-
FROM BORM PARTITION("BORM_2018-06-13") B
-
INNER JOIN LONP L ON L.ACCT_TYPE=B.ACCT_TYPE AND L.INT_CAT=B.INT_CAT
-
INNER JOIN JGDY J ON B.BRANCH_NO = J.JGM AND (J.JGM='1700' OR J.SJJGM='1700')
-
LEFT JOIN CB_ACCT C ON B.ACCT_NO = C.ACCT_NO AND C.SYS_ID = 'BOR'
-
WHERE B.BAD_DEBT_IND in ('02','52')
-
AND B.STAT<>'40'
-
) B
-
LEFT JOIN (SELECT /*+ index(I IDX_INVM_BEC) */ CUSTOMER_NO,ACCT_NO,CURR_VAL FROM
-
INVM PARTITION("INVM_2018-06-13") I
-
INNER JOIN JGDY ON JGDY.JGM=I.BRANCH_NO AND (JGM='1700' OR SJJGM='1700')
-
AND I.ACCT_DESC='S'
-
AND I.CURR_VAL<>0
-
AND I.EXTDATE = DATE'2018-06-13'
-
AND I.CURR_STATUS='00') C ON B.CUSTOMER_NO=C.CUSTOMER_NO
- WHERE (B.P>0 OR B.LX>0.01);
改寫完之後SQL由13分鐘變為5秒鐘執行完,看ID19還要執行999k次,查詢ID18謂詞資訊對應SQL如下,確實是要返回999k行資料。
點選(此處)摺疊或開啟
-
10:59:23 report.QData>SELECT COUNT(*) FROM INVM PARTITION("INVM_2018-06-13") I WHERE I.BRANCH_NO IN (SELECT JGM FROM JGDY WHERE JGM='1700' or SJJGM='1700') AND I.EXTDATE=TO_DATE(' 2018-06-13 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND I.CURR_STATUS='00';
-
COUNT(*)
-
----------
- 999424
| 作者簡介
姚崇·沃趣科技高階資料庫技術專家
熟悉Oracle資料庫內部機制,豐富的資料庫及RAC叢集層故障診斷、效能調優、OWI、資料庫備份恢復及遷移經驗。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28218939/viewspace-2157025/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- SQL優化案例-從執行計劃定位SQL問題(三)SQL優化
- Oracle優化案例-從執行計劃定位SQL問題(三)Oracle優化SQL
- SQL 執行計劃案例1SQL
- SQL最佳化 —— 讀懂執行計劃SQL
- sql 執行計劃SQL
- SQL執行計劃異常引起的效能問題SQL
- SQL執行計劃異常 引起的效能問題SQL
- Oracle sql執行計劃OracleSQL
- SQL Server執行計劃SQLServer
- SQL的執行計劃SQL
- SQL執行計劃分析SQL
- sql最佳化:使用儲存提綱穩定sql執行計劃SQL
- SQL最佳化 | MySQL問題處理案例分享三則MySql
- spark sql語句效能最佳化及執行計劃SparkSQL
- 透過分析SQL語句的執行計劃最佳化SQL(總結)SQL
- SQL最佳化案例-改變那些CBO無能為力的執行計劃(一)SQL
- 獲取SQL執行計劃SQL
- 剖析SQL Server執行計劃SQLServer
- 檢視sql執行計劃SQL
- [轉]透過分析SQL語句的執行計劃最佳化SQL(總結)SQL
- 【sql調優之執行計劃】獲取執行計劃SQL
- 微課sql最佳化(9)、如何獲取執行計劃SQL
- 微課sql最佳化(11) 、如何檢視執行計劃SQL
- 通過執行計劃中的CONCATENATION分析sql問題SQL
- 透過執行計劃中的CONCATENATION分析sql問題SQL
- 使用SQL Profile進行SQL最佳化案例SQL
- sql執行計劃是否改變SQL
- sql的執行計劃 詳解SQL
- sql執行計劃基本命令SQL
- SQL PROFILE修改固定執行計劃SQL
- 使用sql profile固定執行計劃SQL
- 將指定SQL的執行計劃從共享池刪除SQL
- Oracle中檢視已執行sql的執行計劃OracleSQL
- 如何檢視SQL的執行計劃SQL
- 檢視SQL的執行計劃方法SQL
- Oracle 獲取SQL執行計劃方法OracleSQL
- baseline固定SQL執行計劃SQL
- 控制執行計劃之-SQL Profile(一)SQL