[DB2] ลองใช้ Execution Plan เพื่อดูว่า Query ที่เขียนนั้นแย่ หรือไม่

บทความแรกของปี 2016 เลย (จริงๆ Plan ว่าจะเขียนนานแล้ว แต่ติดงานประจำที่ Change บ่อยมากๆ) วันนี้เลยไปเจอ Shell Script ของ AIX ตั้งนึง ที่ทำหน้าดึงข้อมูลมาใส่ใน Temp Table และส่งออกเป็น Text File ไประบบอื่นผ่าน FTP แต่ปัญหา คือ ว่ามันช้ามากกกกกกกกกกกกก รันเกือบ 20 นาทียังไม่ออกเลย

SELECT DISTINCT I1.PID, 
                I1.VALUEDATE, 
                I1.SECURITY_ID, 
                I1.SECURITY_CODE, 
                I2.EFFECTIVE_STARTDATE, 
                I3.EFFECTIVE_ENDDATE, 
                I1.REFERENCE_RATE_CURVE, 
                I1.TENOR, 
                I1.REFERENCE_RATE, 
                I1.SPREAD, 
                I1.FIXED_RATE, 
                I1.TRANS_FLAG, 
                I1.LEG_FLAG, 
                I2.CASHFLOWID AS MINCASHFLOWID 
FROM   INVEST.I_INV_SCHEDULE AS I1 
       INNER JOIN INVEST.I_INV_SCHEDULE AS I2 
               ON I2.VALUEDATE = I1.VALUEDATE 
                  AND I2.SECURITY_ID = I1.SECURITY_ID 
                  AND I2.SECURITY_CODE = I1.SECURITY_CODE 
                  AND I2.CASHFLOWID = (SELECT MIN(I.CASHFLOWID) 
                                       FROM   INVEST.I_INV_SCHEDULE AS I 
                                       WHERE  I.VALUEDATE = I1.VALUEDATE 
                                              AND I.SECURITY_ID = I1.SECURITY_ID 
                                              AND I.SECURITY_CODE = 
                                                  I1.SECURITY_CODE 
                                              AND ( 
                                      ( I.TRANS_FLAG = 'FIXED' 
                                        AND I.FIXED_RATE = 
                                            I1.FIXED_RATE 
                                        AND I.LEG_FLAG IS NULL ) 
                                       OR ( I.TRANS_FLAG = 'FIXED' 
                                            AND I.FIXED_RATE = 
                                                I1.FIXED_RATE 
                                            AND I.LEG_FLAG = 
                                          I1.LEG_FLAG ) 
                                       OR ( I.TRANS_FLAG = 'FLOAT' 
                                            AND I.SPREAD = 
                                                I1.SPREAD 
                                            AND I.REFERENCE_RATE = 
                                                I1.REFERENCE_RATE 
                                            AND I.REFERENCE_RATE_CURVE IS 
                                                NULL 
                                            AND I.TENOR IS NULL 
                                            AND I.LEG_FLAG IS NULL ) 
                                       OR ( I.TRANS_FLAG = 'FLOAT' 
                                            AND I.SPREAD = I1.SPREAD 
                                            AND I.REFERENCE_RATE = 
                                                I1.REFERENCE_RATE 
                                            AND I.REFERENCE_RATE_CURVE IS 
                                                NULL 
                                            AND I.TENOR IS NULL 
                                            AND I.LEG_FLAG = 
                                          I1.LEG_FLAG ) 
                                       OR ( I.TRANS_FLAG = 'FLOAT' 
                                            AND I.SPREAD = I1.SPREAD 
                                            AND I.REFERENCE_RATE_CURVE = 
                                                I1.REFERENCE_RATE_CURVE 
                                            AND I.TENOR = I1.TENOR 
                                            AND I.REFERENCE_RATE IS NULL 
                                            AND I.LEG_FLAG IS NULL ) 
                                       OR ( I.TRANS_FLAG = 'FLOAT' 
                                            AND I.SPREAD = I1.SPREAD 
                                            AND I.REFERENCE_RATE_CURVE = 
                                                I1.REFERENCE_RATE_CURVE 
                                            AND I.TENOR IS NULL 
                                            AND I.REFERENCE_RATE IS NULL 
                                            AND I.LEG_FLAG = 
                                          I1.LEG_FLAG ) )) 
       INNER JOIN INVEST.I_INV_SCHEDULE AS I3 
               ON I3.VALUEDATE = I1.VALUEDATE 
                  AND I3.SECURITY_ID = I1.SECURITY_ID 
                  AND I3.SECURITY_CODE = I1.SECURITY_CODE 
                  AND I3.CASHFLOWID = (SELECT MAX(I.CASHFLOWID) 
                                       FROM   INVEST.I_INV_SCHEDULE AS I 
                                       WHERE  I.VALUEDATE = I1.VALUEDATE 
                                              AND I.SECURITY_ID = I1.SECURITY_ID 
                                              AND I.SECURITY_CODE = 
                                                  I1.SECURITY_CODE 
                                              AND ( 
                                      ( I.TRANS_FLAG = 'FIXED' 
                                        AND I.FIXED_RATE = 
                                            I1.FIXED_RATE 
                                        AND I.LEG_FLAG IS NULL ) 
                                       OR ( I.TRANS_FLAG = 'FIXED' 
                                            AND I.FIXED_RATE = 
                                                I1.FIXED_RATE 
                                            AND I.LEG_FLAG = 
                                          I1.LEG_FLAG ) 
                                       OR ( I.TRANS_FLAG = 'FLOAT' 
                                            AND I.SPREAD = 
                                                I1.SPREAD 
                                            AND I.REFERENCE_RATE = 
                                                I1.REFERENCE_RATE 
                                            AND I.REFERENCE_RATE_CURVE IS 
                                                NULL 
                                            AND I.TENOR IS NULL 
                                            AND I.LEG_FLAG IS NULL ) 
                                       OR ( I.TRANS_FLAG = 'FLOAT' 
                                            AND I.SPREAD = I1.SPREAD 
                                            AND I.REFERENCE_RATE = 
                                                I1.REFERENCE_RATE 
                                            AND I.REFERENCE_RATE_CURVE IS 
                                                NULL 
                                            AND I.TENOR IS NULL 
                                            AND I.LEG_FLAG = 
                                          I1.LEG_FLAG ) 
                                       OR ( I.TRANS_FLAG = 'FLOAT' 
                                            AND I.SPREAD = I1.SPREAD 
                                            AND I.REFERENCE_RATE_CURVE = 
                                                I1.REFERENCE_RATE_CURVE 
                                            AND I.TENOR = I1.TENOR 
                                            AND I.REFERENCE_RATE IS NULL 
                                            AND I.LEG_FLAG IS NULL ) 
                                       OR ( I.TRANS_FLAG = 'FLOAT' 
                                            AND I.SPREAD = I1.SPREAD 
                                            AND I.REFERENCE_RATE_CURVE = 
                                                I1.REFERENCE_RATE_CURVE 
                                            AND I.TENOR IS NULL 
                                            AND I.REFERENCE_RATE IS NULL 
                                            AND I.LEG_FLAG = 
                                          I1.LEG_FLAG ) )) 
WHERE  I1.VALUEDATE = '2015-11-29'
       AND ( ( I2.TRANS_FLAG = 'FIXED' 
               AND I2.FIXED_RATE = I1.FIXED_RATE 
               AND I2.LEG_FLAG IS NULL ) 
              OR ( I2.TRANS_FLAG = 'FIXED' 
                   AND I2.FIXED_RATE = I1.FIXED_RATE 
                   AND I2.LEG_FLAG = I1.LEG_FLAG ) 
              OR ( I2.TRANS_FLAG = 'FLOAT' 
                   AND I2.SPREAD = I1.SPREAD 
                   AND I2.REFERENCE_RATE = I1.REFERENCE_RATE 
                   AND I2.REFERENCE_RATE_CURVE IS NULL 
                   AND I2.TENOR IS NULL 
                   AND I2.LEG_FLAG IS NULL ) 
              OR ( I2.TRANS_FLAG = 'FLOAT' 
                   AND I2.SPREAD = I1.SPREAD 
                   AND I2.REFERENCE_RATE = I1.REFERENCE_RATE 
                   AND I2.REFERENCE_RATE_CURVE IS NULL 
                   AND I2.TENOR IS NULL 
                   AND I2.LEG_FLAG = I1.LEG_FLAG ) 
              OR ( I2.TRANS_FLAG = 'FLOAT' 
                   AND I2.SPREAD = I1.SPREAD 
                   AND I2.REFERENCE_RATE_CURVE = I1.REFERENCE_RATE_CURVE 
                   AND I2.TENOR = I1.TENOR 
                   AND I2.REFERENCE_RATE IS NULL 
                   AND I2.LEG_FLAG IS NULL ) 
              OR ( I2.TRANS_FLAG = 'FLOAT' 
                   AND I2.SPREAD = I1.SPREAD 
                   AND I2.REFERENCE_RATE_CURVE = I1.REFERENCE_RATE_CURVE 
                   AND I2.TENOR IS NULL 
                   AND I2.REFERENCE_RATE IS NULL 
                   AND I2.LEG_FLAG = I1.LEG_FLAG ) ) 
       AND ( ( I3.TRANS_FLAG = 'FIXED' 
               AND I3.FIXED_RATE = I1.FIXED_RATE 
               AND I3.LEG_FLAG IS NULL ) 
              OR ( I3.TRANS_FLAG = 'FIXED' 
                   AND I3.FIXED_RATE = I1.FIXED_RATE 
                   AND I3.LEG_FLAG = I1.LEG_FLAG ) 
              OR ( I3.TRANS_FLAG = 'FLOAT' 
                   AND I3.SPREAD = I1.SPREAD 
                   AND I3.REFERENCE_RATE = I1.REFERENCE_RATE 
                   AND I3.REFERENCE_RATE_CURVE IS NULL 
                   AND I3.TENOR IS NULL 
                   AND I3.LEG_FLAG IS NULL ) 
              OR ( I3.TRANS_FLAG = 'FLOAT' 
                   AND I3.SPREAD = I1.SPREAD 
                   AND I3.REFERENCE_RATE = I1.REFERENCE_RATE 
                   AND I3.REFERENCE_RATE_CURVE IS NULL 
                   AND I3.TENOR IS NULL 
                   AND I3.LEG_FLAG = I1.LEG_FLAG ) 
              OR ( I3.TRANS_FLAG = 'FLOAT' 
                   AND I3.SPREAD = I1.SPREAD 
                   AND I3.REFERENCE_RATE_CURVE = I1.REFERENCE_RATE_CURVE 
                   AND I3.TENOR = I1.TENOR 
                   AND I3.REFERENCE_RATE IS NULL 
                   AND I3.LEG_FLAG IS NULL ) 
              OR ( I3.TRANS_FLAG = 'FLOAT' 
                   AND I3.SPREAD = I1.SPREAD 
                   AND I3.REFERENCE_RATE_CURVE = I1.REFERENCE_RATE_CURVE 
                   AND I3.TENOR IS NULL 
                   AND I3.REFERENCE_RATE IS NULL 
                   AND I3.LEG_FLAG = I1.LEG_FLAG ) ) 
ORDER  BY I1.SECURITY_CODE 
        , MINCASHFLOWID 

จาก Query ตัวอย่าง เราจะพบว่า มัน Self Join เยอะมากก ซึ่งใน DBMS ถ้าตัวเจ๋งๆ มันจะแลาดพอที่จะแปลง Query ให้ดีขึ้น หรืออย่างน้อยมี Execution Plan ช่วยตรวจสอบครับ (ราคา License ของ DB แต่ละเจ้าที่มันแพงๆ ก็เพราะ Feature เหล่านี้แหละ)

งานที่ทำอยู่ DB2 เลยลองใช้ Execution Plan บน Toad และกัน ตัว IBM Data Studio มันโคตรกินแรม 5555

Before

Execution Plan ที่ได้ครับ พบว่ามันมีส่วนของ Table Scan เยอะมากกกกครับ จำเป็นที่จะต้องลง Table Scan ให้น้อยที่สุด โดยการเพิ่ม Index ครับ การเพิ่ม Index ที่ดี เราต้องมาดู Query ที่เราใช้กันบ่อยๆก่อนนะครับ เพราะ ถ้าเพิ่ม Index ไม่เหมาะสมมันส่งผลกับการ Insert/Update นะ และตอนนี้จากการ Scan Query คร่าวๆ ดูแล้วพวกว่ามีการ Self join เยอะครับ โดยดูจาก Field "SECURITY_ID" และ "VALUEDATE" ครับ

CREATE INDEX INVEST.IDX_I_INV_SCHEDULE1
  ON INVEST.I_INV_SCHEDULE
    ( SECURITY_ID ASC, VALUEDATE ASC )
  DISALLOW REVERSE SCANS
  COMPRESS NO
  INCLUDE NULL KEYS;
COMMIT;

Execution Plan หลังจากเพิ่ม Index เร็วขึ้นเหลือ 30 วินาทีครับ และมีการดึงจาก Index แทนการใช้ Table Scan ครับ

After

เปรียบเทียบ Cost ก่อน และหลัง เพิ่ม Index ครับ จากของเดิมที่เป็น TBSCAN (Table Scan ที่ใช้ Cost เยอะมาก) ไปใช้ IXSCAN (Index Scan) แทนครับ

AddIDX-vert

เรื่องนี้เป็น Step ต้นๆ ในการปรับแต่ง Query ให้เร็วขึ้นครับ หากนึกอะไรออก ผมจะทยอยมาเขียนบทความเพิ่มนะครับ ^___^


Discover more from naiwaen@DebuggingSoft

Subscribe to get the latest posts sent to your email.