分頁語句優化案例

某數倉系統,一條SQL語句執行了很長時間,SQL和執行計劃如下

select c.data_dt,
       c.cust_id,
       c.cust_mgr_id,
       c.org_id,
       c.outlets_id,
       c.ent_master_cret_typ,
       c.ent_master_cret_num,
       c.ownership,
       c.org_type,
       c.mt_ind_cd,
       c.mt_ind_cat_cd,
       c.mt_ind_typ_cd,
       c.opac_char,
       c.cust_nb_stat,
       decode(ssa.ft_cust_type, '', 'CRM_IS_FT_FALSE', 'CRM_IS_FT_TRUE') is_ft,
       c.cust_type,
       c.cust_state,
       c.corp_type,
       c.ent_scale,
       c.blg_org,
       c.fst_lvl_brc,
       c.sec_lvl_brc,
       c.outlets,
       c.opac_tm,
       c.clac_tm,
       c.cust_zh_name,
       c.if_c_settle_acct,
       c.rest_co_time,
       c.ncust_categ,
       c.if_opac,
       c.clac_reas,
       c.fst_lvl_brc_id,
       c.sec_lvl_brc_id,
       c.sleep_cust_time,
       c.indus_sub_cls,
       c.valid_crdt_limit_amt,
       c.use_crt_bal,
       c.mid_biz_income / 10000 as mid_biz_income,
       c.lan_bal / 10000 as lan_bal,
       c.lan_dmv / 10000 as lan_dmv,
       c.aet_bal / 10000 as aet_bal,
       c.e_bill_aet_bal / 10000 as e_bill_aet_bal,
       c.aet_sum / 10000 as aet_sum,
       c.e_bill_aet_sum / 10000 as e_bill_aet_sum,
       c.disc_bal / 10000 as disc_bal,
       c.e_bill_disc_bal / 10000 as e_bill_disc_bal,
       c.disc_sum / 10000 as disc_sum,
       c.profit_eva / 10000 as profit_eva,
       c.e_bill_disc_sum / 10000 as e_bill_disc_sum,
       c.dep_bal / 10000 as dep_bal,
       c.dep_dmv / 10000 as dep_dmv,
       c.cust_sum_settle_qty / 10000 as cust_sum_settle_qty,
       c.int_settle_qty / 10000 as int_settle_qty,
       c.ep_bank_settle_qty / 10000 as ep_bank_settle_qty,
       ta.cust_mgr_id as cust_mgr_id_add,
       case ta.cust_mgr_id
         when c.cust_mgr_id then
          q.name
         else
          q.name || '(' || tb.name || ')'
       end AS cust_mgr_name_new,
       c.outlets as org_name3,
       q.name,
       h.org_name as org_name1,
       f.org_name as org_name2,
       p.org_name,
       c.settle_times_l,
       c.settle_times_m,
       fx.flag,
       li.f_value CUSTOMERJD
  from ssss.a_a_aaaa_sum c
  left join scrm.AAAA_AAAA_AAAA_AAA_N s
    on c.data_dt = s.crm_dt
   and c.cust_id = s.cust_id
  left join ocrm.AAAA_AAA_AAAAA_ITEM li
    on li.f_code = s.custjd
   and li.f_lookup_id = 'CRM_CUST_LIFESTATE'
  left join AAAA_A_AI_AAAA_DESC ta
    on c.cust_id = ta.cust_id
   and ta.cust_mgr_id = c.cust_mgr_id
  left join ocrm.AAAA_AAA_AAALOYEE_CHECK q
    on q.sequest_id = c.cust_mgr_id
  left join ocrm.AAAA_AAA_AAALOYEE_CHECK tb
    on ta.cust_mgr_id = tb.sequest_id
  left join ocrm.AAAAA_AAAA_AAA p
    on p.org_id = c.org_id
  left join ocrm.AAAAA_AAAA_AAA h
    on h.org_id = c.fst_lvl_brc_id
  left join ocrm.AAAAA_AAAA_AAA f
    on f.org_id = c.sec_lvl_brc_id
  left join (select max(cust_no) cust_no, max(ft_cust_type) ft_cust_type
               from scrm.A_M_FT_CUST_BASE_SUM ssb
              group by cust_no) ssa
    on ssa.cust_no = c.cust_id
  join SYS_UNITS ao
    on ao.UNITID = c.org_id
  left join ocrm.AAAA_A_AI_AAAA_AAARISKLVL fx
    on c.cust_id = fx.custid
 where 1 = 1
   AND ao.unitseq like '%B9999%'
   and (c.corp_type = 'CRM_COMPANYTYPE_02' or
       c.corp_type = 'CRM_COMPANYTYPE_01')
   AND C.DATA_DT = '20180930'
   and (ao.UNITSEQ like '%B9999%' or ao.UNITSEQ like '%9999000000%' or
       ao.UNITSEQ like '%B0200%' or ao.UNITSEQ like '%B0300%' or
       ao.UNITSEQ like '%B0400%' or ao.UNITSEQ like '%B0500%' or
       ao.UNITSEQ like '%B0600%' or ao.UNITSEQ like '%B0700%' or
       ao.UNITSEQ like '%B0800%' or ao.UNITSEQ like '%B0900%' or
       ao.UNITSEQ like '%B1000%' or ao.UNITSEQ like '%B1100%' or
       ao.UNITSEQ like '%B1200%' or ao.UNITSEQ like '%B1300%' or
       ao.UNITSEQ like '%B1400%' or ao.UNITSEQ like '%B1500%' or
       ao.UNITSEQ like '%B1600%' or ao.UNITSEQ like '%B1700%' or
       ao.UNITSEQ like '%B1800%' or ao.UNITSEQ like '%B1900%' or
       ao.UNITSEQ like '%B2000%' or ao.UNITSEQ like '%B2100%' or
       ao.UNITSEQ like '%B2200%' or ao.UNITSEQ like '%B2300%' or
       ao.UNITSEQ like '%B2400%' or ao.UNITSEQ like '%B2500%' or
       ao.UNITSEQ like '%B2900%' or ao.UNITSEQ like '%B3000%' or
       ao.UNITSEQ like '%B3100%' or ao.UNITSEQ like '%B3200%' or
       ao.UNITSEQ like '%B3400%' or ao.UNITSEQ like '%B3500%' or
       ao.UNITSEQ like '%B3700%' or ao.UNITSEQ like '%B4700%' or
       ao.UNITSEQ like '%B5100%' or ao.UNITSEQ like '%B5500%' or
       ao.UNITSEQ like '%B7600%' or ao.UNITSEQ like '%B8000%' or
       ao.UNITSEQ like '%B8300%' or ao.UNITSEQ like '%B8400%' or
       ao.UNITSEQ like '%B9704%' or ao.UNITSEQ like '%B9705%' or
       ao.UNITSEQ like '%DEPB999%')
 order by c.cust_id desc;

select * from table(dbms_xplan.display);
Plan hash value: 4062436817
 
-------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                       | Name                          | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                                |                               |     1 |  1877 |    49   (7)| 00:00:01 |       |       |
|   1 |  SORT ORDER BY                                  |                               |     1 |  1877 |    49   (7)| 00:00:01 |       |       |
|   2 |   NESTED LOOPS OUTER                            |                               |     1 |  1877 |    48   (5)| 00:00:01 |       |       |
|   3 |    NESTED LOOPS OUTER                           |                               |     1 |  1848 |    47   (5)| 00:00:01 |       |       |
|   4 |     NESTED LOOPS OUTER                          |                               |     1 |  1819 |    46   (5)| 00:00:01 |       |       |
|   5 |      NESTED LOOPS OUTER                         |                               |     1 |  1790 |    45   (5)| 00:00:01 |       |       |
|   6 |       NESTED LOOPS OUTER                        |                               |     1 |  1771 |    44   (5)| 00:00:01 |       |       |
|   7 |        NESTED LOOPS OUTER                       |                               |     1 |  1743 |    43   (5)| 00:00:01 |       |       |
|   8 |         NESTED LOOPS OUTER                      |                               |     1 |  1709 |    42   (5)| 00:00:01 |       |       |
|*  9 |          HASH JOIN OUTER                        |                               |     1 |  1681 |    41   (5)| 00:00:01 |       |       |
|  10 |           NESTED LOOPS                          |                               |       |       |            |          |       |       |
|  11 |            NESTED LOOPS                         |                               |     1 |  1637 |     5   (0)| 00:00:01 |       |       |
|  12 |             NESTED LOOPS OUTER                  |                               |     1 |  1586 |     4   (0)| 00:00:01 |       |       |
|  13 |              NESTED LOOPS OUTER                 |                               |     1 |  1518 |     3   (0)| 00:00:01 |       |       |
|  14 |               PARTITION RANGE SINGLE            |                               |     1 |  1464 |     2   (0)| 00:00:01 |   435 |   435 |
|* 15 |                TABLE ACCESS FULL                | A_A_A_AAAA_AAAA_SUM           |     1 |  1464 |     2   (0)| 00:00:01 |   435 |   435 |
|  16 |               PARTITION RANGE SINGLE            |                               |     1 |    54 |     1   (0)| 00:00:01 |   399 |   399 |
|* 17 |                TABLE ACCESS BY LOCAL INDEX ROWID| AAAA_AAAA_AAAA_AAA_N          |     1 |    54 |     1   (0)| 00:00:01 |   399 |   399 |
|* 18 |                 INDEX RANGE SCAN                | IDX_AAAA_AAAA_AAAA_AAA_N      |     1 |       |     1   (0)| 00:00:01 |   399 |   399 |
|* 19 |              TABLE ACCESS BY INDEX ROWID        | AAAA_AAA_AAAAA_ITEM          |     1 |    68 |     1   (0)| 00:00:01 |       |       |
|* 20 |               INDEX RANGE SCAN                  | IDX_AAAA_AAA_AAAAA_ITEM      |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 21 |             INDEX RANGE SCAN                    | INDEX_UID                     |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 22 |            TABLE ACCESS BY INDEX ROWID          | SYS_UNITS                     |     1 |    51 |     1   (0)| 00:00:01 |       |       |
|  23 |           VIEW                                  |                               |   306 | 13464 |    36   (6)| 00:00:01 |       |       |
|  24 |            HASH GROUP BY                        |                               |   306 | 10098 |    36   (6)| 00:00:01 |       |       |
|  25 |             INDEX FAST FULL SCAN                | XPK自貿客戶基礎彙總表         | 12377 |   398K|    34   (0)| 00:00:01 |       |       |
|  26 |          TABLE ACCESS BY INDEX ROWID            | AAAA_AAA_AAALOYEE_CHECK       |     1 |    28 |     1   (0)| 00:00:01 |       |       |
|* 27 |           INDEX RANGE SCAN                      | IDX_EMPLOYEE_CHECK_SEQ        |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 28 |         TABLE ACCESS BY INDEX ROWID             | AAAA_A_AI_AAAA_DESC           |     1 |    34 |     1   (0)| 00:00:01 |       |       |
|* 29 |          INDEX UNIQUE SCAN                      | PK_F_CI_CUST_DESC             |     1 |       |     1   (0)| 00:00:01 |       |       |
|  30 |        TABLE ACCESS BY INDEX ROWID              | AAAA_AAA_AAALOYEE_CHECK       |     1 |    28 |     1   (0)| 00:00:01 |       |       |
|* 31 |         INDEX RANGE SCAN                        | IDX_EMPLOYEE_CHECK_SEQ        |     1 |       |     1   (0)| 00:00:01 |       |       |
|  32 |       TABLE ACCESS BY INDEX ROWID               | AAAA_A_AI_AAAA_AAARISKLVL     |     1 |    19 |     1   (0)| 00:00:01 |       |       |
|* 33 |        INDEX RANGE SCAN                         | IDX_AAAA_A_AI_AAAA_AAARISKLVL |     1 |       |     1   (0)| 00:00:01 |       |       |
|  34 |      TABLE ACCESS BY INDEX ROWID                | AAAAA_AAAA_AAA                |     1 |    29 |     1   (0)| 00:00:01 |       |       |
|* 35 |       INDEX RANGE SCAN                          | IDX_AAAAA_AAAA_AAA_ORGID      |     1 |       |     1   (0)| 00:00:01 |       |       |
|  36 |     TABLE ACCESS BY INDEX ROWID                 | AAAAA_AAAA_AAA                |     1 |    29 |     1   (0)| 00:00:01 |       |       |
|* 37 |      INDEX RANGE SCAN                           | IDX_AAAAA_AAAA_AAA_ORGID      |     1 |       |     1   (0)| 00:00:01 |       |       |
|  38 |    TABLE ACCESS BY INDEX ROWID                  | AAAAA_AAAA_AAA                |     1 |    29 |     1   (0)| 00:00:01 |       |       |
|* 39 |     INDEX RANGE SCAN                            | IDX_AAAAA_AAAA_AAA_ORGID      |     1 |       |     1   (0)| 00:00:01 |       |       |
-------------------------------------------------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   9 - access("SSA"."CUST_NO"(+)="CUST_NO")
  15 - filter(("ENT_TYPE"='CRM_COMPANYTYPE_01' OR "ENT_TYPE"='CRM_COMPANYTYPE_02') AND "CRM_DT"='20180930')
  17 - filter("S"."CRM_DT"(+)='20180930')
  18 - access("CUST_NO"="S"."CUST_ID"(+))
  19 - filter("LI"."F_LOOKUP_ID"(+)='CRM_CUST_LIFESTATE')
  20 - access("LI"."F_CODE"(+)="S"."CUSTJD")
  21 - access("AO"."UNITID"="ORG_NO")
  22 - filter("AO"."UNITSEQ" LIKE '%B9999%' AND "AO"."UNITSEQ" IS NOT NULL)
  27 - access("Q"."SEQUEST_ID"(+)="CUST_MGR_ID_ID")
  28 - filter("TA"."CUST_MGR_ID"(+)="CUST_MGR_ID_ID")
  29 - access("CUST_NO"="TA"."CUST_ID"(+))
  31 - access("TA"."CUST_MGR_ID"="TB"."SEQUEST_ID"(+))
  33 - access("CUST_NO"="FX"."CUSTID"(+))
  35 - access("F"."ORG_ID"(+)="SEC_LVL_BRC_B_NO")
  37 - access("H"."ORG_ID"(+)="FST_LVL_BRC_B_NO")
  39 - access("P"."ORG_ID"(+)="ORG_NO")

數倉系統,所涉及到的表都很大(這裏沒有用腳本來取表信息,因爲這裏不是案例的重點,往下看),在執行計劃裏面很多ROWS爲1,還是統計信息過期~,導致了一堆的nest loop 如果想看哪一個nest loop有問題,需要獲取驅動表的返回行數,太麻煩!!!!因爲涉及到表太多,逐一收集統計信息同樣太麻煩,所以使用HINT來改變一下執行計劃,HINT的寫法/*+ ordered cardinality(c 1000000) cardinality(s 1000000) use_hash(c s li ta q tb p h f ssa ao fx) */  走HASH的執行計劃雖然不是最好的,肯定也是性能比較好。加HINT之後的執行計劃如下:

Plan hash value: 1492224931
 
--------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                          | Name                      | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                   |                           |     1 |  1877 |  6781   (1)| 00:01:22 |       |       |
|   1 |  SORT ORDER BY                     |                           |     1 |  1877 |  6781   (1)| 00:01:22 |       |       |
|*  2 |   HASH JOIN OUTER                  |                           |     1 |  1877 |  6780   (1)| 00:01:22 |       |       |
|*  3 |    HASH JOIN                       |                           |     1 |  1858 |  5054   (1)| 00:01:01 |       |       |
|*  4 |     HASH JOIN OUTER                |                           |     1 |  1807 |  5034   (1)| 00:01:01 |       |       |
|*  5 |      HASH JOIN OUTER               |                           |     1 |  1763 |  4998   (1)| 00:01:00 |       |       |
|*  6 |       HASH JOIN OUTER              |                           |     1 |  1734 |  4986   (1)| 00:01:00 |       |       |
|*  7 |        HASH JOIN OUTER             |                           |     1 |  1705 |  4975   (1)| 00:01:00 |       |       |
|*  8 |         HASH JOIN OUTER            |                           |     1 |  1676 |  4963   (1)| 00:01:00 |       |       |
|*  9 |          HASH JOIN OUTER           |                           |     1 |  1648 |  4689   (1)| 00:00:57 |       |       |
|* 10 |           HASH JOIN OUTER          |                           |     1 |  1620 |  4414   (1)| 00:00:53 |       |       |
|* 11 |            HASH JOIN OUTER         |                           |     1 |  1586 |   210   (1)| 00:00:03 |       |       |
|* 12 |             HASH JOIN OUTER        |                           |     1 |  1518 |     5  (20)| 00:00:01 |       |       |
|  13 |              PARTITION RANGE SINGLE|                           |     1 |  1464 |     2   (0)| 00:00:01 |   435 |   435 |
|* 14 |               TABLE ACCESS FULL    | A_A_A_AAAA_AAAA_SUM       |     1 |  1464 |     2   (0)| 00:00:01 |   435 |   435 |
|  15 |              PARTITION RANGE SINGLE|                           |     1 |    54 |     2   (0)| 00:00:01 |   399 |   399 |
|* 16 |               TABLE ACCESS FULL    | AAAA_AAAA_AAAA_AAA_N      |     1 |    54 |     2   (0)| 00:00:01 |   399 |   399 |
|* 17 |             TABLE ACCESS FULL      | AAAA_AAA_AAAAA_ITEM      |     9 |   612 |   205   (1)| 00:00:03 |       |       |
|  18 |            TABLE ACCESS FULL       | AAAA_A_AI_AAAA_DESC       |   594K|    19M|  4200   (1)| 00:00:51 |       |       |
|  19 |           TABLE ACCESS FULL        | AAAA_AAA_AAALOYEE_CHECK   | 76583 |  2094K|   273   (1)| 00:00:04 |       |       |
|  20 |          TABLE ACCESS FULL         | AAAA_AAA_AAALOYEE_CHECK   | 76583 |  2094K|   273   (1)| 00:00:04 |       |       |
|  21 |         TABLE ACCESS FULL          | AAAAA_AAAA_AAA            |  3692 |   104K|    11   (0)| 00:00:01 |       |       |
|  22 |        TABLE ACCESS FULL           | AAAAA_AAAA_AAA            |  3692 |   104K|    11   (0)| 00:00:01 |       |       |
|  23 |       TABLE ACCESS FULL            | AAAAA_AAAA_AAA            |  3692 |   104K|    11   (0)| 00:00:01 |       |       |
|  24 |      VIEW                          |                           |   306 | 13464 |    36   (6)| 00:00:01 |       |       |
|  25 |       HASH GROUP BY                |                           |   306 | 10098 |    36   (6)| 00:00:01 |       |       |
|  26 |        INDEX FAST FULL SCAN        | XPK自貿客戶基礎彙總表     | 12377 |   398K|    34   (0)| 00:00:01 |       |       |
|* 27 |     TABLE ACCESS FULL              | SYS_UNITS                 |   185 |  9435 |    19   (0)| 00:00:01 |       |       |
|  28 |    TABLE ACCESS FULL               | AAAA_A_AI_AAAA_AAARISKLVL |   999K|    18M|  1720   (1)| 00:00:21 |       |       |
--------------------------------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("CUST_NO"="FX"."CUSTID"(+))
   3 - access("AO"."UNITID"="ORG_NO")
   4 - access("SSA"."CUST_NO"(+)="CUST_NO")
   5 - access("F"."ORG_ID"(+)="SEC_LVL_BRC_B_NO")
   6 - access("H"."ORG_ID"(+)="FST_LVL_BRC_B_NO")
   7 - access("P"."ORG_ID"(+)="ORG_NO")
   8 - access("TA"."CUST_MGR_ID"="TB"."SEQUEST_ID"(+))
   9 - access("Q"."SEQUEST_ID"(+)="CUST_MGR_ID_ID")
  10 - access("TA"."CUST_MGR_ID"(+)="CUST_MGR_ID_ID" AND "CUST_NO"="TA"."CUST_ID"(+))
  11 - access("LI"."F_CODE"(+)="S"."CUSTJD")
  12 - access("CUST_NO"="S"."CUST_ID"(+) AND "CRM_DT"="S"."CRM_DT"(+))
  14 - filter(("ENT_TYPE"='CRM_COMPANYTYPE_01' OR "ENT_TYPE"='CRM_COMPANYTYPE_02') AND "CRM_DT"='20180930')
  16 - filter("S"."CRM_DT"(+)='20180930')
  17 - filter("LI"."F_LOOKUP_ID"(+)='CRM_CUST_LIFESTATE')
  27 - filter("AO"."UNITSEQ" LIKE '%B9999%' AND "AO"."UNITSEQ" IS NOT NULL)

果然SQL在幾分鐘之內出結果了~本以爲完事了。卻被告知SQL需要30s之內出結果纔行,要不就報超時了~

“我很肯定,這個數據量,不可能在30s之內出結果,除非是分頁!!”

“就是分頁!我想着分頁不分頁都沒有影響,所以分頁的部分沒截給你”

“當然,有影響。區別大了,分頁自有分頁的方法去搞!”

然後原SQL取給我了

SELECT *
  FROM (SELECT ROW_NUMBER() OVER(ORDER BY 1) AS RN, BUSINESS_QUERY.*
          FROM (select c.data_dt,
                       c.cust_id,
                       c.cust_mgr_id,
                       c.org_id,
                       c.outlets_id,
                       c.ent_master_cret_typ,
                       c.ent_master_cret_num,
                       c.ownership,
                       c.org_type,
                       c.mt_ind_cd,
                       c.mt_ind_cat_cd,
                       c.mt_ind_typ_cd,
                       c.opac_char,
                       c.cust_nb_stat,
                       decode(ssa.ft_cust_type,
                              '',
                              'CRM_IS_FT_FALSE',
                              'CRM_IS_FT_TRUE') is_ft,
                       c.cust_type,
                       c.cust_state,
                       c.corp_type,
                       c.ent_scale,
                       c.blg_org,
                       c.fst_lvl_brc,
                       c.sec_lvl_brc,
                       c.outlets,
                       c.opac_tm,
                       c.clac_tm,
                       c.cust_zh_name,
                       c.if_c_settle_acct,
                       c.rest_co_time,
                       c.ncust_categ,
                       c.if_opac,
                       c.clac_reas,
                       c.fst_lvl_brc_id,
                       c.sec_lvl_brc_id,
                       c.sleep_cust_time,
                       c.indus_sub_cls,
                       c.valid_crdt_limit_amt,
                       c.use_crt_bal,
                       c.mid_biz_income / 10000 as mid_biz_income,
                       c.lan_bal / 10000 as lan_bal,
                       c.lan_dmv / 10000 as lan_dmv,
                       c.aet_bal / 10000 as aet_bal,
                       c.e_bill_aet_bal / 10000 as e_bill_aet_bal,
                       c.aet_sum / 10000 as aet_sum,
                       c.e_bill_aet_sum / 10000 as e_bill_aet_sum,
                       c.disc_bal / 10000 as disc_bal,
                       c.e_bill_disc_bal / 10000 as e_bill_disc_bal,
                       c.disc_sum / 10000 as disc_sum,
                       c.profit_eva / 10000 as profit_eva,
                       c.e_bill_disc_sum / 10000 as e_bill_disc_sum,
                       c.dep_bal / 10000 as dep_bal,
                       c.dep_dmv / 10000 as dep_dmv,
                       c.cust_sum_settle_qty / 10000 as cust_sum_settle_qty,
                       c.int_settle_qty / 10000 as int_settle_qty,
                       c.ep_bank_settle_qty / 10000 as ep_bank_settle_qty,
                       ta.cust_mgr_id as cust_mgr_id_add,
                       case ta.cust_mgr_id
                         when c.cust_mgr_id then
                          q.name
                         else
                          q.name || '(' || tb.name || ')'
                       end AS cust_mgr_name_new,
                       c.outlets as org_name3,
                       q.name,
                       h.org_name as org_name1,
                       f.org_name as org_name2,
                       p.org_name,
                       c.settle_times_l,
                       c.settle_times_m,
                       fx.flag,
                       li.f_value CUSTOMERJD
                  from ssss.a_a_aaaa_sum c
                  left join scrm.AAAA_AAAA_AAAA_AAA_N s
                    on c.data_dt = s.crm_dt
                   and c.cust_id = s.cust_id
                  left join ocrm.AAAA_AAA_AAAAA_ITEM li
                    on li.f_code = s.custjd
                   and li.f_lookup_id = 'CRM_CUST_LIFESTATE'
                  left join AAAA_A_AI_AAAA_DESC ta
                    on c.cust_id = ta.cust_id
                   and ta.cust_mgr_id = c.cust_mgr_id
                  left join ocrm.AAAA_AAA_AAALOYEE_CHECK q
                    on q.sequest_id = c.cust_mgr_id
                  left join ocrm.AAAA_AAA_AAALOYEE_CHECK tb
                    on ta.cust_mgr_id = tb.sequest_id
                  left join ocrm.AAAAA_AAAA_AAA p
                    on p.org_id = c.org_id
                  left join ocrm.AAAAA_AAAA_AAA h
                    on h.org_id = c.fst_lvl_brc_id
                  left join ocrm.AAAAA_AAAA_AAA f
                    on f.org_id = c.sec_lvl_brc_id
                  left join (select max(cust_no) cust_no,
                                   max(ft_cust_type) ft_cust_type
                              from scrm.A_M_FT_CUST_BASE_SUM ssb
                             group by cust_no) ssa
                    on ssa.cust_no = c.cust_id
                  join SYS_UNITS ao
                    on ao.UNITID = c.org_id
                  left join ocrm.AAAA_A_AI_AAAA_AAARISKLVL fx
                    on c.cust_id = fx.custid
                 where 1 = 1
                   AND ao.unitseq like '%B9999%'
                   and (c.corp_type = 'CRM_COMPANYTYPE_02' or
                       c.corp_type = 'CRM_COMPANYTYPE_01')
                   AND C.DATA_DT = '20180930'
                   and (ao.UNITSEQ like '%B9999%' or
                       ao.UNITSEQ like '%9999000000%' or
                       ao.UNITSEQ like '%B0200%' or
                       ao.UNITSEQ like '%B0300%' or
                       ao.UNITSEQ like '%B0400%' or
                       ao.UNITSEQ like '%B0500%' or
                       ao.UNITSEQ like '%B0600%' or
                       ao.UNITSEQ like '%B0700%' or
                       ao.UNITSEQ like '%B0800%' or
                       ao.UNITSEQ like '%B0900%' or
                       ao.UNITSEQ like '%B1000%' or
                       ao.UNITSEQ like '%B1100%' or
                       ao.UNITSEQ like '%B1200%' or
                       ao.UNITSEQ like '%B1300%' or
                       ao.UNITSEQ like '%B1400%' or
                       ao.UNITSEQ like '%B1500%' or
                       ao.UNITSEQ like '%B1600%' or
                       ao.UNITSEQ like '%B1700%' or
                       ao.UNITSEQ like '%B1800%' or
                       ao.UNITSEQ like '%B1900%' or
                       ao.UNITSEQ like '%B2000%' or
                       ao.UNITSEQ like '%B2100%' or
                       ao.UNITSEQ like '%B2200%' or
                       ao.UNITSEQ like '%B2300%' or
                       ao.UNITSEQ like '%B2400%' or
                       ao.UNITSEQ like '%B2500%' or
                       ao.UNITSEQ like '%B2900%' or
                       ao.UNITSEQ like '%B3000%' or
                       ao.UNITSEQ like '%B3100%' or
                       ao.UNITSEQ like '%B3200%' or
                       ao.UNITSEQ like '%B3400%' or
                       ao.UNITSEQ like '%B3500%' or
                       ao.UNITSEQ like '%B3700%' or
                       ao.UNITSEQ like '%B4700%' or
                       ao.UNITSEQ like '%B5100%' or
                       ao.UNITSEQ like '%B5500%' or
                       ao.UNITSEQ like '%B7600%' or
                       ao.UNITSEQ like '%B8000%' or
                       ao.UNITSEQ like '%B8300%' or
                       ao.UNITSEQ like '%B8400%' or
                       ao.UNITSEQ like '%B9704%' or
                       ao.UNITSEQ like '%B9705%' or
                       ao.UNITSEQ like '%DEPB999%')
                 order by c.cust_id desc) BUSINESS_QUERY) SUB_QUERY
 WHERE RN BETWEEN 1 AND 20;

看到SQL語句,慌了

這個語句沒法優化,因爲分頁的框架不對。這種框架能實現分頁的功能,卻無法實現高性能的分頁。爲什麼?

分頁語句是優先返回多少頁,每頁固定行。其實大多數人都只會點擊前幾頁,後面的幾乎不點,分頁的框架利用了這個優勢能夠快速響應前多少頁,後面的頁數越來越慢。而上面的SQL語句分頁,每一頁的響應時間都一樣……搞清楚他們的需求之後,果斷改寫SQL語句,變成我們可優化的分頁框架

SELECT *
  FROM (SELECT BUSINESS_QUERY.*,rownum rn                   --ROW_NUMBER() OVER(ORDER BY 1) AS RN, BUSINESS_QUERY.*
          FROM (select /*+ index(c idx_a_a_aaaa_sum_02) use_nl(ssa) */c.data_dt,
                       c.cust_id,
                       c.cust_mgr_id,
                       c.cust_sum_settle_qty / 10000 as cust_sum_settle_qty,
                       c.int_settle_qty / 10000 as int_settle_qty,
                       c.ep_bank_settle_qty / 10000 as ep_bank_settle_qty,
                       ta.cust_mgr_id as cust_mgr_id_add,
                       case ta.cust_mgr_id
                         when c.cust_mgr_id then
                          q.name
                         else
                          q.name || '(' || tb.name || ')'
                       end AS cust_mgr_name_new,
                       c.outlets as org_name3,
                       q.name,
                       h.org_name as org_name1,
                       f.org_name as org_name2,
                       p.org_name,
                       c.settle_times_l,
                       c.settle_times_m,
                       fx.flag,
                       li.f_value CUSTOMERJD
                  from ssss.a_a_aaaa_sum c
                  left join scrm.AAAA_AAAA_AAAA_AAA_N s
                    on c.data_dt = s.crm_dt
                   and c.cust_id = s.cust_id
                  left join ocrm.AAAA_AAA_AAAAA_ITEM li
                    on li.f_code = s.custjd
                   and li.f_lookup_id = 'CRM_CUST_LIFESTATE'
                  left join AAAA_A_AI_AAAA_DESC ta
                    on c.cust_id = ta.cust_id
                   and ta.cust_mgr_id = c.cust_mgr_id
                  left join ocrm.AAAA_AAA_AAALOYEE_CHECK q
                    on q.sequest_id = c.cust_mgr_id
                  left join ocrm.AAAA_AAA_AAALOYEE_CHECK tb
                    on ta.cust_mgr_id = tb.sequest_id
                  left join ocrm.AAAAA_AAAA_AAA p
                    on p.org_id = c.org_id
                  left join ocrm.AAAAA_AAAA_AAA h
                    on h.org_id = c.fst_lvl_brc_id
                  left join ocrm.AAAAA_AAAA_AAA f
                    on f.org_id = c.sec_lvl_brc_id
                  left join (select max(cust_no) cust_no,
                                   max(ft_cust_type) ft_cust_type
                              from scrm.A_M_FT_CUST_BASE_SUM ssb
                             group by cust_no) ssa
                    on ssa.cust_no = c.cust_id
                  join SYS_UNITS ao
                    on ao.UNITID = c.org_id
                  left join ocrm.AAAA_A_AI_AAAA_AAARISKLVL fx
                    on c.cust_id = fx.custid
                 where 1 = 1
                   AND ao.unitseq like '%B9999%'
                   and (c.corp_type = 'CRM_COMPANYTYPE_02' or
                       c.corp_type = 'CRM_COMPANYTYPE_01')
                   AND C.DATA_DT = '20180930'
                 order by c.cust_id desc) BUSINESS_QUERY where rownum<=20) SUB_QUERY
                 where rn>=0;

同時創建索引消除sort order by帶來的性能開銷


create index ssss.idx_a_a_aaaa_sum_02 on scrm.A_A_A_AAAA_AAAA_SUM(crm_dt,cust_no desc,ent_type) parallel 16;

alter index ssss.idx_a_a_aaaa_sum_02 noparallel

我之前的博客提過分頁語句的優化技巧https://blog.csdn.net/Skybig1988/article/details/46670055 

先總結一下分頁語句的特點及優化技巧:
1.分頁語句一般情況下服務於頁面請求,對於響應時間要求高,一旦響應時間過長,客戶不明所以的可能會反覆點擊。對數據庫造成類似於“阻塞”的等待
2.分頁語句裏面必須含有order by,如果沒有order by可以說分頁是沒有意義的
3.分頁語句的主要消耗在order by,優化分頁語句主要目的就是消除排序帶來的性能問題
4.因爲索引在創建的時候已經排序,所以可以通過在order by的列上面建索引的方式來抵消排序
5.建索引的時候升序或者降序 最好和order by後面的升序或者降序保持一致
6.如果SQL語句還有額外的過濾,建組合索引的時候應該把過濾列放在第一位
綜上所述:分頁語句優化的精髓就是通過建索引抵消掉排序帶來的性能開銷。

我之前畫了一張優化分頁語句的流程圖:

優化之後SQL 1s出結果,執行計劃如下

Plan hash value: 1457464028
 
--------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                        | Name                          | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                                 |                               |     1 |   687 |   240K  (1)| 00:48:01 |       |       |
|*  1 |  VIEW                                            |                               |     1 |   687 |   240K  (1)| 00:48:01 |       |       |
|*  2 |   COUNT STOPKEY                                  |                               |       |       |            |          |       |       |
|   3 |    VIEW                                          |                               |     1 |   674 |   240K  (1)| 00:48:01 |       |       |
|   4 |     NESTED LOOPS OUTER                           |                               |     1 |   625 |   240K  (1)| 00:48:01 |       |       |
|   5 |      NESTED LOOPS OUTER                          |                               |     1 |   597 |   240K  (1)| 00:48:01 |       |       |
|   6 |       NESTED LOOPS OUTER                         |                               |     1 |   578 |   240K  (1)| 00:48:01 |       |       |
|   7 |        NESTED LOOPS OUTER                        |                               |     1 |   544 |   240K  (1)| 00:48:01 |       |       |
|   8 |         NESTED LOOPS OUTER                       |                               |     1 |   516 |   240K  (1)| 00:48:01 |       |       |
|   9 |          NESTED LOOPS OUTER                      |                               |     1 |   487 |   240K  (1)| 00:48:00 |       |       |
|  10 |           NESTED LOOPS OUTER                     |                               |     1 |   458 |   239K  (1)| 00:48:00 |       |       |
|  11 |            NESTED LOOPS OUTER                    |                               |     1 |   429 |   239K  (1)| 00:48:00 |       |       |
|  12 |             NESTED LOOPS                         |                               |     1 |   412 |   239K  (1)| 00:48:00 |       |       |
|  13 |              NESTED LOOPS OUTER                  |                               |     1 |   361 |   239K  (1)| 00:48:00 |       |       |
|  14 |               NESTED LOOPS OUTER                 |                               |     1 |   293 |   239K  (1)| 00:48:00 |       |       |
|  15 |                TABLE ACCESS BY GLOBAL INDEX ROWID| A_A_A_AAAA_AAAA_SUM           |     1 |   239 |   239K  (1)| 00:48:00 |   435 |   435 |
|* 16 |                 INDEX RANGE SCAN                 | IDX_A_A_CUST_SUM_02           |     1 |       |  2168   (1)| 00:00:27 |       |       |
|  17 |                PARTITION RANGE SINGLE            |                               |     1 |    54 |     1   (0)| 00:00:01 |   399 |   399 |
|* 18 |                 TABLE ACCESS BY LOCAL INDEX ROWID| AAAA_AAAA_AAAA_AAA_N          |     1 |    54 |     1   (0)| 00:00:01 |   399 |   399 |
|* 19 |                  INDEX RANGE SCAN                | IDX_AAAA_AAAA_AAAA_AAA_N      |     1 |       |     1   (0)| 00:00:01 |   399 |   399 |
|* 20 |               TABLE ACCESS BY INDEX ROWID        | AAAA_AAA_AAAAA_ITEM          |     1 |    68 |     1   (0)| 00:00:01 |       |       |
|* 21 |                INDEX RANGE SCAN                  | IDX_AAAA_AAA_AAAAA_ITEM      |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 22 |              TABLE ACCESS BY INDEX ROWID         | SYS_UNITS                     |     1 |    51 |     1   (0)| 00:00:01 |       |       |
|* 23 |               INDEX RANGE SCAN                   | INDEX_UID                     |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 24 |             VIEW                                 |                               |     3 |    51 |    36   (6)| 00:00:01 |       |       |
|  25 |              SORT GROUP BY                       |                               |   306 | 10098 |    36   (6)| 00:00:01 |       |       |
|  26 |               INDEX FAST FULL SCAN               | XPK自貿客戶基礎彙總表         | 12377 |   398K|    34   (0)| 00:00:01 |       |       |
|  27 |            TABLE ACCESS BY INDEX ROWID           | AAAAA_AAAA_AAA                |     1 |    29 |     1   (0)| 00:00:01 |       |       |
|* 28 |             INDEX RANGE SCAN                     | IDX_AAAAA_AAAA_AAA_ORGID      |     1 |       |     1   (0)| 00:00:01 |       |       |
|  29 |           TABLE ACCESS BY INDEX ROWID            | AAAAA_AAAA_AAA                |     1 |    29 |     1   (0)| 00:00:01 |       |       |
|* 30 |            INDEX RANGE SCAN                      | IDX_AAAAA_AAAA_AAA_ORGID      |     1 |       |     1   (0)| 00:00:01 |       |       |
|  31 |          TABLE ACCESS BY INDEX ROWID             | AAAAA_AAAA_AAA                |     1 |    29 |     1   (0)| 00:00:01 |       |       |
|* 32 |           INDEX RANGE SCAN                       | IDX_AAAAA_AAAA_AAA_ORGID      |     1 |       |     1   (0)| 00:00:01 |       |       |
|  33 |         TABLE ACCESS BY INDEX ROWID              | AAAA_AAA_AAALOYEE_CHECK       |     1 |    28 |     1   (0)| 00:00:01 |       |       |
|* 34 |          INDEX RANGE SCAN                        | IDX_EMPLOYEE_CHECK_SEQ        |     1 |       |     1   (0)| 00:00:01 |       |       |
|* 35 |        TABLE ACCESS BY INDEX ROWID               | AAAA_A_AI_AAAA_DESC           |     1 |    34 |     1   (0)| 00:00:01 |       |       |
|* 36 |         INDEX UNIQUE SCAN                        | PK_F_CI_CUST_DESC             |     1 |       |     1   (0)| 00:00:01 |       |       |
|  37 |       TABLE ACCESS BY INDEX ROWID                | AAAA_A_AI_AAAA_AAARISKLVL     |     1 |    19 |     1   (0)| 00:00:01 |       |       |
|* 38 |        INDEX RANGE SCAN                          | IDX_AAAA_A_AI_AAAA_AAARISKLVL |     1 |       |     1   (0)| 00:00:01 |       |       |
|  39 |      TABLE ACCESS BY INDEX ROWID                 | AAAA_AAA_AAALOYEE_CHECK       |     1 |    28 |     1   (0)| 00:00:01 |       |       |
|* 40 |       INDEX RANGE SCAN                           | IDX_EMPLOYEE_CHECK_SEQ        |     1 |       |     1   (0)| 00:00:01 |       |       |
--------------------------------------------------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter("RN">=0)
   2 - filter(ROWNUM<=20)
  16 - access("CRM_DT"='20180930')
       filter("ENT_TYPE"='CRM_COMPANYTYPE_01' OR "ENT_TYPE"='CRM_COMPANYTYPE_02')
  18 - filter("S"."CRM_DT"(+)='20180930')
  19 - access(SYS_OP_UNDESCEND(SYS_OP_DESCEND("CUST_NO"))="S"."CUST_ID"(+))
  20 - filter("LI"."F_LOOKUP_ID"(+)='CRM_CUST_LIFESTATE')
  21 - access("LI"."F_CODE"(+)="S"."CUSTJD")
  22 - filter("AO"."UNITSEQ" LIKE '%B9999%' AND "AO"."UNITSEQ" IS NOT NULL)
  23 - access("AO"."UNITID"="ORG_NO")
  24 - filter("SSA"."CUST_NO"(+)=SYS_OP_UNDESCEND(SYS_OP_DESCEND("CUST_NO")))
  28 - access("P"."ORG_ID"(+)="ORG_NO")
  30 - access("H"."ORG_ID"(+)="FST_LVL_BRC_B_NO")
  32 - access("F"."ORG_ID"(+)="SEC_LVL_BRC_B_NO")
  34 - access("Q"."SEQUEST_ID"(+)="CUST_MGR_ID_ID")
  35 - filter("TA"."CUST_MGR_ID"(+)="CUST_MGR_ID_ID")
  36 - access(SYS_OP_UNDESCEND(SYS_OP_DESCEND("CUST_NO"))="TA"."CUST_ID"(+))
  38 - access(SYS_OP_UNDESCEND(SYS_OP_DESCEND("CUST_NO"))="FX"."CUSTID"(+))
  40 - access("TA"."CUST_MGR_ID"="TB"."SEQUEST_ID"(+))

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章