1. 程式人生 > >再一次用merge優化update

再一次用merge優化update

程式碼上線前稽核,某銀行理財系統一條update執行了一個多小時……

改寫前的SQL以及執行計劃
update fnc.t1_cust_sign_info s
   set s.is_wealth =
       (select case
                 when ttt.total_amt >=
                      (select sp.paravalue
                         from fnc.sys_param sp
                        where sp.moduleid = '5'
                          and sp.paraid = '60000007') then
                  '2'
                 when ttt.total_amt <
                      (select sp.paravalue
                         from fnc.sys_param sp
                        where sp.moduleid = '5'
                          and sp.paraid = '60000007') and
                      ttt.total_amt >=
                      (select sp.paravalue
                         from fnc.sys_param sp
                        where sp.moduleid = '5'
                          and sp.paraid = '60000006') then
                  '0'
                 else
                  s.is_wealth
               end
          from (select t2.cust_no, sum(t2.app_amt) total_amt
                  from (select /*+ index(t idx_kf2_1 )*/t.cust_no,
                               t.app_amt * nvl(cur.standard_price, 1) app_amt
                          from fnc.t5_cust_trans_log t
                          left join (SELECT f2.foreign_cur,f2.standard_price
                                       FROM (SELECT f1.foreign_cur, f1.file_time, f1.standard_price,
                                                 MAX(f1.file_time)over(partition BY f1.foreign_cur ) max_file_time
                                            FROM fnc.t5_foreign_cur f1) f2
                                      WHERE f2.file_time = f2.max_file_time)cur
                            on cur.foreign_cur = t.cur
                         where t.busi_code in ('130', '122')
                           and t.trans_status = '0'--測試階段改為'0',線上要改成'3'
                           and t.cust_type = '3'
                        union all
                        select  /*+ index(t idx_kf2_2 )*/t.cust_no,
                               t.total_vol * nvl(p.nav, 1) * nvl(cur.standard_price, 1) app_amt
                          from fnc.t5_cust_vol t --與表t1_cust_info關聯列 cust_no需要建立索引
                          left join fnc.t5_prod_info p
                            on p.prod_code = t.prod_code
                          left join (SELECT f2.foreign_cur,f2.standard_price
                                       FROM (SELECT f1.foreign_cur, f1.file_time, f1.standard_price,
                                                 MAX(f1.file_time)over(partition BY f1.foreign_cur ) max_file_time
                                            FROM fnc.t5_foreign_cur f1) f2
                                      WHERE f2.file_time = f2.max_file_time)cur
                            on cur.foreign_cur = p.prod_currency
                          left join fnc.t1_cust_info c
                            on c.cust_no = t.cust_no
                         where c.cust_type = '3') t2
                 group by t2.cust_no) ttt
         where ttt.cust_no = s.cust_no)
 where exists (select 1
          from (select t.cust_no, t.app_amt
                  from fnc.t5_cust_trans_log t
                 where t.busi_code in ('130', '122')
                   and t.trans_status = '0'--測試階段改為'0',線上要改成'3'
                   and t.cust_type = '3') t2
         where t2.cust_no = s.cust_no);

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------
Plan hash value: 2887642830

---------------------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name                    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT                    |                         |  4252 |   116K|    27M  (1)| 91:17:25 |
|   1 |  UPDATE                             | T1_CUST_SIGN_INFO       |       |       |            |          |
|*  2 |   HASH JOIN RIGHT SEMI              |                         |  4252 |   116K|  4185   (1)| 00:00:51 |
|   3 |    INLIST ITERATOR                  |                         |       |       |            |          |
|*  4 |     TABLE ACCESS BY INDEX ROWID     | T5_CUST_TRANS_LOG       |  2962 | 50354 |  2882   (1)| 00:00:35 |
|*  5 |      INDEX RANGE SCAN               | IX_T5_CUST_TRANS_LOG_02 |  3543 |       |    68   (0)| 00:00:01 |
|   6 |    TABLE ACCESS FULL                | T1_CUST_SIGN_INFO       |  1337K|    14M|  1296   (2)| 00:00:16 |
|   7 |   VIEW                              |                         |     2 |    46 |  6439   (1)| 00:01:18 |
|   8 |    SORT GROUP BY                    |                         |     2 |    44 |  6439   (1)| 00:01:18 |
|   9 |     VIEW                            |                         |   768 | 16896 |  6439   (1)| 00:01:18 |
|  10 |      UNION-ALL                      |                         |       |       |            |          |
|* 11 |       HASH JOIN OUTER               |                         |   388 | 30264 |  2987   (1)| 00:00:36 |
|  12 |        INLIST ITERATOR              |                         |       |       |            |          |
|* 13 |         TABLE ACCESS BY INDEX ROWID | T5_CUST_TRANS_LOG       |     1 |    25 |  2882   (1)| 00:00:35 |
|* 14 |          INDEX RANGE SCAN           | IX_T5_CUST_TRANS_LOG_02 |  3543 |       |    68   (0)| 00:00:01 |
|* 15 |        VIEW                         |                         |  9952 |   515K|   105   (2)| 00:00:02 |
|  16 |         WINDOW SORT                 |                         |  9952 |   281K|   105   (2)| 00:00:02 |
|  17 |          TABLE ACCESS FULL          | T5_FOREIGN_CUR          |  9952 |   281K|   103   (0)| 00:00:02 |
|* 18 |       HASH JOIN OUTER               |                         |   380 | 42940 |  3452   (1)| 00:00:42 |
|  19 |        NESTED LOOPS OUTER           |                         |     1 |    60 |  3347   (1)| 00:00:41 |
|  20 |         NESTED LOOPS                |                         |     1 |    38 |  3345   (1)| 00:00:41 |
|* 21 |          TABLE ACCESS BY INDEX ROWID| T1_CUST_INFO            |     1 |    11 |     3   (0)| 00:00:01 |
|* 22 |           INDEX UNIQUE SCAN         | PK_T1_CUST_INFO         |     1 |       |     2   (0)| 00:00:01 |
|* 23 |          TABLE ACCESS FULL          | T5_CUST_VOL             |     1 |    27 |  3342   (1)| 00:00:41 |
|  24 |         TABLE ACCESS BY INDEX ROWID | T5_PROD_INFO            |     1 |    22 |     2   (0)| 00:00:01 |
|* 25 |          INDEX RANGE SCAN           | PK_T5_PROD_INFO         |     1 |       |     1   (0)| 00:00:01 |
|* 26 |        VIEW                         |                         |  9952 |   515K|   105   (2)| 00:00:02 |
|  27 |         WINDOW SORT                 |                         |  9952 |   281K|   105   (2)| 00:00:02 |
|  28 |          TABLE ACCESS FULL          | T5_FOREIGN_CUR          |  9952 |   281K|   103   (0)| 00:00:02 |
|  29 |   TABLE ACCESS BY INDEX ROWID       | SYS_PARAM               |     1 |    15 |     1   (0)| 00:00:01 |
|* 30 |    INDEX UNIQUE SCAN                | PK_SYS_PARAM            |     1 |       |     0   (0)| 00:00:01 |
|  31 |    TABLE ACCESS BY INDEX ROWID      | SYS_PARAM               |     1 |    15 |     1   (0)| 00:00:01 |
|* 32 |     INDEX UNIQUE SCAN               | PK_SYS_PARAM            |     1 |       |     0   (0)| 00:00:01 |
|  33 |     TABLE ACCESS BY INDEX ROWID     | SYS_PARAM               |     1 |    15 |     1   (0)| 00:00:01 |
|* 34 |      INDEX UNIQUE SCAN              | PK_SYS_PARAM            |     1 |       |     0   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("T"."CUST_NO"="S"."CUST_NO")
   4 - filter("T"."CUST_NO" IS NOT NULL AND "T"."CUST_TYPE"='3')
   5 - access(("T"."BUSI_CODE"='122' OR "T"."BUSI_CODE"='130') AND "T"."TRANS_STATUS"='0')
  11 - access("F2"."FOREIGN_CUR"(+)="T"."CUR")
  13 - filter("T"."CUST_NO"=:B1 AND "T"."CUST_TYPE"='3')
  14 - access(("T"."BUSI_CODE"='122' OR "T"."BUSI_CODE"='130') AND "T"."TRANS_STATUS"='0')
  15 - filter("F2"."FILE_TIME"(+)="F2"."MAX_FILE_TIME"(+))
  18 - access("F2"."FOREIGN_CUR"(+)="P"."PROD_CURRENCY")
  21 - filter("C"."CUST_TYPE"='3')
  22 - access("C"."CUST_NO"=:B1)
  23 - filter("T"."CUST_NO"=:B1 AND "C"."CUST_NO"="T"."CUST_NO")
  25 - access("P"."PROD_CODE"(+)="T"."PROD_CODE")
  26 - filter("F2"."FILE_TIME"(+)="F2"."MAX_FILE_TIME"(+))
  30 - access("SP"."PARAID"='60000007' AND "SP"."MODULEID"='5')
  32 - access("SP"."PARAID"='60000007' AND "SP"."MODULEID"='5')
  34 - access("SP"."PARAID"='60000006' AND "SP"."MODULEID"='5')
OWNER      OBJECT_TYPE          OBJECT_NAME                    ALIAS                                    PAR    SIZE_MB   NUM_ROWS ESTIMATE_P LAST_ANALYZE STA
---------- -------------------- ------------------------------ ---------------------------------------- --- ---------- ---------- ---------- ------------ ---
FNC        TABLE                SYS_PARAM                      SP | SP | SP                             NO       .0625         77 100%       30-SEP-16    NO
FNC        TABLE                T1_CUST_INFO                   C                                        NO         299    1297140 100%       19-JUL-16    NO
FNC        TABLE                T1_CUST_SIGN_INFO              C | S                                    NO          37    1337492 100%       30-SEP-16    NO
FNC        TABLE                T5_CUST_TRANS_LOG              T | T                                    NO         216       6342 100%       30-SEP-16    YES
FNC        TABLE                T5_CUST_VOL                    T                                        NO          96     528534 100%       30-SEP-16    NO
FNC        TABLE                T5_FOREIGN_CUR                 F1 | F1                                  NO           3       9952 100%       09-SEP-16    NO
FNC        TABLE                T5_PROD_INFO                   P                                        NO           5      14497 100%       30-SEP-16    NO
FNC        INDEX                IX_T5_CUST_TRANS_LOG_02        NOALIAS | NOALIAS                        NO          28       6345 100%       30-SEP-16    YES
FNC        INDEX (UNIQUE)       PK_SYS_PARAM                   NOALIAS | NOALIAS | NOALIAS              NO       .0625         77 100%       30-SEP-16    NO

SQL> SELECT COUNT(1) FROM T5_CUST_TRANS_LOG;

  COUNT(1)
----------
      7511
改寫後的SQL以及執行計劃
merge into t1_cust_sign_info c
using (select distinct a.rid,a.cust_no,
                      case
                         when b.total_amt >=
                              (select sp.paravalue
                                 from sys_param sp
                                where sp.moduleid = '5'
                                  and sp.paraid = '60000007') then
                          '2'
                         when b.total_amt <
                              (select sp.paravalue
                                 from sys_param sp
                                where sp.moduleid = '5'
                                  and sp.paraid = '60000007') and
                              b.total_amt >=
                              (select sp.paravalue
                                 from sys_param sp
                                where sp.moduleid = '5'
                                  and sp.paraid = '60000006') then
                          '0'
                         else
                          a.is_wealth
                       end as is_wealth
                from ( select s.rowid rid, s.cust_no,s.is_wealth
                        from t1_cust_sign_info s, t5_cust_trans_log t
                        where s.cust_no = t.cust_no
                         and t.busi_code in ('130', '122')
                        and t.trans_status = '0'--測試階段改為'0',線上要改成'3'
                        and t.cust_type = '3') a
                left join (select t2.cust_no, sum(t2.app_amt) total_amt
                            from (select t.cust_no,
                                         t.app_amt * nvl(cur.standard_price, 1) app_amt
                                    from t5_cust_trans_log t
                                    left join (SELECT f2.foreign_cur,f2.standard_price
                                                 FROM (SELECT f1.foreign_cur, f1.file_time, f1.standard_price,
                                                           MAX(f1.file_time)over(partition BY f1.foreign_cur ) max_file_time
                                                      FROM t5_foreign_cur f1) f2
                                                WHERE f2.file_time = f2.max_file_time)cur
                                      on cur.foreign_cur = t.cur
                                   where t.busi_code in ('130', '122')
                                     and t.trans_status = '0'--測試階段改為'0',線上要改成'3'
                                     and t.cust_type = '3'
                                  union all
                                  select t.cust_no,
                                         t.total_vol * nvl(p.nav, 1) * nvl(cur.standard_price, 1) app_amt
                                    from t5_cust_vol t --與表t1_cust_info關聯列 cust_no需要建立索引
                                    left join t5_prod_info p
                                      on p.prod_code = t.prod_code
                                    left join (SELECT f2.foreign_cur,f2.standard_price
                                                 FROM (SELECT f1.foreign_cur, f1.file_time, f1.standard_price,
                                                           MAX(f1.file_time)over(partition BY f1.foreign_cur ) max_file_time
                                                      FROM t5_foreign_cur f1) f2
                                                WHERE f2.file_time = f2.max_file_time)cur
                                      on cur.foreign_cur = p.prod_currency
                                    left join t1_cust_info c
                                      on c.cust_no = t.cust_no
                                   where c.cust_type = '3') t2
                           group by t2.cust_no) b
                  on a.cust_no = b.cust_no ) d
on(d.rid = c.rowid)
when matched then
  update set c.is_wealth = d.is_wealth;

4570 rows merged. 
Elapsed: 00:00:05.40


PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------
Plan hash value: 966278225

---------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                               | Name                    | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------------------------------
|   0 | MERGE STATEMENT                         |                         |  2962 | 11848 |       | 33791   (2)| 00:06:46 |
|   1 |  MERGE                                  | T1_CUST_SIGN_INFO       |       |       |       |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID           | SYS_PARAM               |     1 |    15 |       |     1   (0)| 00:00:01 |
|*  3 |    INDEX UNIQUE SCAN                    | PK_SYS_PARAM            |     1 |       |       |     0   (0)| 00:00:01 |
|   4 |    TABLE ACCESS BY INDEX ROWID          | SYS_PARAM               |     1 |    15 |       |     1   (0)| 00:00:01 |
|*  5 |     INDEX UNIQUE SCAN                   | PK_SYS_PARAM            |     1 |       |       |     0   (0)| 00:00:01 |
|   6 |     TABLE ACCESS BY INDEX ROWID         | SYS_PARAM               |     1 |    15 |       |     1   (0)| 00:00:01 |
|*  7 |      INDEX UNIQUE SCAN                  | PK_SYS_PARAM            |     1 |       |       |     0   (0)| 00:00:01 |
|   8 |   VIEW                                  |                         |       |       |       |            |          |
|*  9 |    HASH JOIN                            |                         |  2962 |   118K|       | 33791   (2)| 00:06:46 |
|  10 |     VIEW                                |                         |  2962 | 62202 |       | 32484   (1)| 00:06:30 |
|  11 |      SORT UNIQUE                        |                         |  2962 |   170K|       | 32484   (1)| 00:06:30 |
|* 12 |       HASH JOIN OUTER                   |                         |  2962 |   170K|       | 32483   (1)| 00:06:30 |
|* 13 |        HASH JOIN                        |                         |  2962 |   107K|       |  4185   (1)| 00:00:51 |
|  14 |         INLIST ITERATOR                 |                         |       |       |       |            |          |
|* 15 |          TABLE ACCESS BY INDEX ROWID    | T5_CUST_TRANS_LOG       |  2962 | 50354 |       |  2882   (1)| 00:00:35 |
|* 16 |           INDEX RANGE SCAN              | IX_T5_CUST_TRANS_LOG_02 |  3543 |       |       |    68   (0)| 00:00:01 |
|  17 |         TABLE ACCESS FULL               | T1_CUST_SIGN_INFO       |  1337K|    25M|       |  1296   (2)| 00:00:16 |
|  18 |        VIEW                             |                         |   344K|  7397K|       | 28295   (2)| 00:05:40 |
|  19 |         SORT GROUP BY                   |                         |   344K|  7397K|    95M| 28295   (2)| 00:05:40 |
|  20 |          VIEW                           |                         |  3104K|    65M|       | 19434   (1)| 00:03:54 |
|  21 |           UNION-ALL                     |                         |       |       |       |            |          |
|* 22 |            HASH JOIN OUTER              |                         |  1650K|   122M|       |  2996   (1)| 00:00:36 |
|  23 |             INLIST ITERATOR             |                         |       |       |       |            |          |
|* 24 |              TABLE ACCESS BY INDEX ROWID| T5_CUST_TRANS_LOG       |  3348 | 83700 |       |  2882   (1)| 00:00:35 |
|* 25 |               INDEX RANGE SCAN          | IX_T5_CUST_TRANS_LOG_02 |  3543 |       |       |    68   (0)| 00:00:01 |
|* 26 |             VIEW                        |                         |  9952 |   515K|       |   105   (2)| 00:00:02 |
|  27 |              WINDOW SORT                |                         |  9952 |   281K|       |   105   (2)| 00:00:02 |
|  28 |               TABLE ACCESS FULL         | T5_FOREIGN_CUR          |  9952 |   281K|       |   103   (0)| 00:00:02 |
|* 29 |            HASH JOIN RIGHT OUTER        |                         |  1453K|   156M|       | 16438   (1)| 00:03:18 |
|* 30 |             VIEW                        |                         |  9952 |   515K|       |   105   (2)| 00:00:02 |
|  31 |              WINDOW SORT                |                         |  9952 |   281K|       |   105   (2)| 00:00:02 |
|  32 |               TABLE ACCESS FULL         | T5_FOREIGN_CUR          |  9952 |   281K|       |   103   (0)| 00:00:02 |
|* 33 |             HASH JOIN RIGHT OUTER       |                         |   528K|    30M|       | 16325   (1)| 00:03:16 |
|  34 |              TABLE ACCESS FULL          | T5_PROD_INFO            | 14497 |   311K|       |   175   (1)| 00:00:03 |
|* 35 |              HASH JOIN                  |                         |   528K|    19M|    19M| 16147   (1)| 00:03:14 |
|  36 |               TABLE ACCESS FULL         | T5_CUST_VOL             |   528K|    13M|       |  3341   (1)| 00:00:41 |
|* 37 |               TABLE ACCESS FULL         | T1_CUST_INFO            |  1293K|    13M|       | 10404   (1)| 00:02:05 |
|  38 |     TABLE ACCESS FULL                   | T1_CUST_SIGN_INFO       |  1337K|    25M|       |  1299   (2)| 00:00:16 |
---------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("SP"."PARAID"='60000007' AND "SP"."MODULEID"='5')
   5 - access("SP"."PARAID"='60000007' AND "SP"."MODULEID"='5')
   7 - access("SP"."PARAID"='60000006' AND "SP"."MODULEID"='5')
   9 - access("D"."RID"="C".ROWID)
  12 - access("S"."CUST_NO"="B"."CUST_NO"(+))
  13 - access("S"."CUST_NO"="T"."CUST_NO")
  15 - filter("T"."CUST_NO" IS NOT NULL AND "T"."CUST_TYPE"='3')
  16 - access(("T"."BUSI_CODE"='122' OR "T"."BUSI_CODE"='130') AND "T"."TRANS_STATUS"='0')
  22 - access("F2"."FOREIGN_CUR"(+)="T"."CUR")
  24 - filter("T"."CUST_TYPE"='3')
  25 - access(("T"."BUSI_CODE"='122' OR "T"."BUSI_CODE"='130') AND "T"."TRANS_STATUS"='0')
  26 - filter("F2"."FILE_TIME"(+)="F2"."MAX_FILE_TIME"(+))
  29 - access("F2"."FOREIGN_CUR"(+)="P"."PROD_CURRENCY")
  30 - filter("F2"."FILE_TIME"(+)="F2"."MAX_FILE_TIME"(+))
  33 - access("P"."PROD_CODE"(+)="T"."PROD_CODE")
  35 - access("C"."CUST_NO"="T"."CUST_NO")
  37 - filter("C"."CUST_TYPE"='3')

66 rows selected.

優化後 5秒執行完 而原來執行一個小時都不出結果!!!