某數倉系統,一條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"(+))