不懂業務的SQL優化方法

flzhang發表於2015-06-18

看著如下長長的SQL,對於其要實現的功能一無所知,開發人員需要根據領導的需要調整該SQL並匯出產生的資料,但每次執行都要5分鐘左右,對對於開發,除錯和領導確認都不能接受,開發人員直接將此SQL轉給我優化,由於此SQL並不寫入應用且手工執行頻率很高,於是最直接的方法就是先並行加快sql的執行速度
WITH T_MAST AS( 
SELECT 
        T3.BASE_YW
        ,T2.USER_ID
        ,T2.USER_NM   
        ,T2.BRNC_ID
        ,(SELECT SORG_NM FROM MCS_HQ.MA_SORG T WHERE T.SORG_ID = T2.BRNC_ID) BRNC_NM
        ,T2.OFFC_ID
        ,(SELECT SORG_NM FROM MCS_HQ.MA_SORG T WHERE T.SORG_ID = T2.OFFC_ID) OFFC_NM
        ,(SELECT CODE_NM FROM MCS_HQ.CD_CODE_LIST T WHERE T.CODE_DIV = 'USER_TP' AND T.LANG_CD = 'CN' AND T.CODE_CD = T2.USER_TP) USER_TP_NM
        ,COUNT(*)    LONG_CNT
from    MCS_HQ.HI_USER_MENU_ACCS_LOG T1
        ,MCS_HQ.MA_USER T2
        ,MCS_HQ.MA_BASE_YMD T3
WHERE   T1.MENU_ID IN ('M001164','M001126','M001125','M001124','M001123','M001163')
AND     T1.USER_ID = T2.USER_ID
AND     T1.ACCS_DT >= DATE '2014-12-29'
AND     TO_CHAR(T1.ACCS_DT,'yyyymmDD') = T3.BASE_YMD
AND     T2.USER_TP IN ('OFFC','SLMN','BRNC')  --指定使用者
AND     T2.USER_USE_ST = 'ACTV'
AND     T2.USER_ID NOT IN ('chokyu','kimbk01') --('CHOKU','KIMBK01');
AND EXISTS(SELECT * FROM MCS_HQ.MA_USER_PROD A WHERE A.USER_PROD_REL_TP = 'MPRD' AND A.USE_YN = 'Y'
AND A.REL_TO_YMD = '99991231' AND A.PROD_CD IN ('GSM','LTE','CDMA','TABLET') AND A.USER_ID = T1.USER_ID)
AND     EXISTS(
            SELECT  *
            FROM    MCS_HQ.MA_USER_ROLE A
                    ,MCS_HQ.MA_ROLE_MENU B
            WHERE   A.USER_ID = T1.USER_ID
            AND     A.USE_YN = 'Y'
            AND     B.ROLE_ID = A.ROLE_ID
            AND     B.USE_YN = 'Y'
            AND     B.MENU_ID IN ('M001164','M001126','M001125','M001124','M001123','M001163')
        )
AND     T2.SUBS_ID = 'SCIC'
GROUP BY T3.BASE_YW
        ,T2.BRNC_ID
        ,T2.OFFC_ID
        ,T2.USER_ID
        ,T2.USER_NM
        ,T2.USER_TP
)
SELECT   MAX(BRNC_NM) BRNC_NM
        ,MAX(OFFC_NM) OFFC_NM
        ,USER_ID USER_ID
        ,MAX(USER_NM) USER_NM   
        ,USER_TP_NM
        ,sum(DECODE(BASE_YW,'201501',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201502',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201503',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201504',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201505',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201506',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201507',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201508',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201509',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201510',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201511',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201512',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201513',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201514',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201515',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201516',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201517',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201518',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201519',LONG_CNT)) W20_USER_CNT
        ,sum(DECODE(BASE_YW,'201520',LONG_CNT)) W20_LOGN_CNT
        ,sum(DECODE(BASE_YW,'201521',LONG_CNT)) W21_LOGN_CNT
        ,sum(DECODE(BASE_YW,'201522',LONG_CNT)) W22_LOGN_CNT
        ,sum(DECODE(BASE_YW,'201523',LONG_CNT)) W23_LOGN_CNT
        ,sum(DECODE(BASE_YW,'201524',LONG_CNT)) W24_LOGN_CNT
        ,sum(DECODE(BASE_YW,'201525',LONG_CNT)) W25_LOGN_CNT
FROM    T_MAST
GROUP BY BRNC_ID
        ,OFFC_ID
        ,USER_ID
        ,USER_TP_NM
ORDER BY
    BRNC_NM
    ,OFFC_NM
    ,USER_ID
    ,USER_TP_NM
要想盡快確認哪些表需要並行提高執行速度,先檢視執行計劃

SELECT STATEMENT, GOAL = FIRST_ROWS   48563 1 248  
 INDEX RANGE SCAN MCS_HQ UX_MA_SORG_3 1 1 16  
 INDEX RANGE SCAN MCS_HQ UX_MA_SORG_3 1 1 16  
 INDEX RANGE SCAN MCS_HQ UX_CD_CODE_LIST_1 2 1 34  
 SORT ORDER BY   48563 1 248  
  HASH GROUP BY   48563 1 248  
   VIEW MCS_HQ_READ  48561 1 248  
    HASH GROUP BY   48561 1 135  
     NESTED LOOPS SEMI   48560 1 135  
      NESTED LOOPS   48554 1 103  
       NESTED LOOPS   48552 1 56  
        HASH JOIN RIGHT SEMI   48551 1 40  
         VIEW SYS VW_SQ_1 481 37509 412599  
          HASH JOIN   481 37509 1912959  
           TABLE ACCESS FULL MCS_HQ MA_ROLE_MENU 58 145 3480 ("B"."MENU_ID"='M001123' OR "B"."MENU_ID"='M001124' OR "B"."MENU_ID"='M001125' OR "B"."MENU_ID"='M001126' OR "B"."MENU_ID"='M001163' OR "B"."MENU_ID"='M001164') AND "B"."USE_YN"='Y' 
           TABLE ACCESS FULL MCS_HQ MA_USER_ROLE 422 158708 4285116 "A"."USE_YN"='Y' AND "A"."USER_ID"<>'chokyu' AND "A"."USER_ID"<>'kimbk01' 
         INLIST ITERATOR       
          TABLE ACCESS BY INDEX ROWID MCS_HQ HI_USER_MENU_ACCS_LOG 48069 56598 1641342 "T1"."USER_ID"<>'chokyu' AND "T1"."USER_ID"<>'kimbk01' 
           INDEX RANGE SCAN MCS_HQ X_HI_USER_MENU_ACCS_LOG_1 658 57249   
        INDEX RANGE SCAN MCS_HQ UX_MA_BASE_YMD_1 1 1 16  
       TABLE ACCESS BY INDEX ROWID MCS_HQ MA_USER 2 1 47 "T2"."USER_USE_ST"='ACTV' AND ("T2"."USER_TP"='BRNC' OR "T2"."USER_TP"='OFFC' OR "T2"."USER_TP"='SLMN') AND "T2"."SUBS_ID"='SCIC' 
        INDEX UNIQUE SCAN MCS_HQ PK_MA_USER 1 1  "T2"."USER_ID"<>'chokyu' AND "T2"."USER_ID"<>'kimbk01' 
      INLIST ITERATOR       
       TABLE ACCESS BY INDEX ROWID MCS_HQ MA_USER_PROD 6 384534 12305088 "A"."USE_YN"='Y' AND "A"."REL_TO_YMD"='99991231' 
        INDEX UNIQUE SCAN MCS_HQ PK_MA_USER_PROD 5 1  "A"."USER_ID"<>'chokyu' AND "A"."USER_ID"<>'kimbk01'
從計劃中不難看出MA_ROLE_MENU,MA_USER_ROLE先做了全表掃描,然後又和HI_USER_MENU_ACCS_LOG做了 HASH JOIN RIGHT SEMI 連線,且做連線時消耗的成本極高,於是設法
在這兩部採用並行,提高執行效率,增加並行後的sql如下

WITH T_MAST AS( 
SELECT  /*+PARALLEL(T1,8)*/
        T3.BASE_YW
        ,T2.USER_ID
        ,T2.USER_NM   
        ,T2.BRNC_ID
        ,(SELECT SORG_NM FROM MCS_HQ.MA_SORG T WHERE T.SORG_ID = T2.BRNC_ID) BRNC_NM
        ,T2.OFFC_ID
        ,(SELECT SORG_NM FROM MCS_HQ.MA_SORG T WHERE T.SORG_ID = T2.OFFC_ID) OFFC_NM
        ,(SELECT CODE_NM FROM MCS_HQ.CD_CODE_LIST T WHERE T.CODE_DIV = 'USER_TP' AND T.LANG_CD = 'CN' AND T.CODE_CD = T2.USER_TP) USER_TP_NM
        ,COUNT(*)    LONG_CNT
from    MCS_HQ.HI_USER_MENU_ACCS_LOG T1
        ,MCS_HQ.MA_USER T2
        ,MCS_HQ.MA_BASE_YMD T3
WHERE   T1.MENU_ID IN ('M001164','M001126','M001125','M001124','M001123','M001163')
AND     T1.USER_ID = T2.USER_ID
AND     T1.ACCS_DT >= DATE '2014-12-29'
AND     TO_CHAR(T1.ACCS_DT,'yyyymmDD') = T3.BASE_YMD
AND     T2.USER_TP IN ('OFFC','SLMN','BRNC')  --三星使用者
AND     T2.USER_USE_ST = 'ACTV'
AND     T2.USER_ID NOT IN ('chokyu','kimbk01') --('CHOKU','KIMBK01');
AND EXISTS(SELECT * FROM MCS_HQ.MA_USER_PROD A WHERE A.USER_PROD_REL_TP = 'MPRD' AND A.USE_YN = 'Y'
AND A.REL_TO_YMD = '99991231' AND A.PROD_CD IN ('GSM','LTE','CDMA','TABLET') AND A.USER_ID = T1.USER_ID)
AND     EXISTS(
            SELECT  /*+PARALLEL(A,8) PARALLEL(B,8)*/*
            FROM    MCS_HQ.MA_USER_ROLE A
                    ,MCS_HQ.MA_ROLE_MENU B
            WHERE   A.USER_ID = T1.USER_ID
            AND     A.USE_YN = 'Y'
            AND     B.ROLE_ID = A.ROLE_ID
            AND     B.USE_YN = 'Y'
            AND     B.MENU_ID IN ('M001164','M001126','M001125','M001124','M001123','M001163')
        )
AND     T2.SUBS_ID = 'SCIC'
GROUP BY T3.BASE_YW
        ,T2.BRNC_ID
        ,T2.OFFC_ID
        ,T2.USER_ID
        ,T2.USER_NM
        ,T2.USER_TP
)
SELECT   MAX(BRNC_NM) BRNC_NM
        ,MAX(OFFC_NM) OFFC_NM
        ,USER_ID USER_ID
        ,MAX(USER_NM) USER_NM   
        ,USER_TP_NM
        ,sum(DECODE(BASE_YW,'201501',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201502',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201503',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201504',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201505',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201506',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201507',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201508',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201509',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201510',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201511',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201512',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201513',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201514',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201515',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201516',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201517',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201518',LONG_CNT)) W20_USER_CNT
         ,sum(DECODE(BASE_YW,'201519',LONG_CNT)) W20_USER_CNT
        ,sum(DECODE(BASE_YW,'201520',LONG_CNT)) W20_LOGN_CNT
        ,sum(DECODE(BASE_YW,'201521',LONG_CNT)) W21_LOGN_CNT
        ,sum(DECODE(BASE_YW,'201522',LONG_CNT)) W22_LOGN_CNT
        ,sum(DECODE(BASE_YW,'201523',LONG_CNT)) W23_LOGN_CNT
        ,sum(DECODE(BASE_YW,'201524',LONG_CNT)) W24_LOGN_CNT
        ,sum(DECODE(BASE_YW,'201525',LONG_CNT)) W25_LOGN_CNT
FROM    T_MAST
GROUP BY BRNC_ID
        ,OFFC_ID
        ,USER_ID
        ,USER_TP_NM
ORDER BY
    BRNC_NM
    ,OFFC_NM
    ,USER_ID
    ,USER_TP_NM
這樣增加hint後的sql執行速度在10s內完成,基本適應頻率較高的手動執行和更改也能滿足領導及時檢查的需要


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/750077/viewspace-1704185/,如需轉載,請註明出處,否則將追究法律責任。

相關文章