今天盖尔找我优化一条SQL,SQL如下:
SELECT DISTINCT b.organ_id,
c.company_name as organ_name,
a.distri_date,
a.distri_type,
d.TYPE_NAME Capital_name,
b.policy_code,
b.apply_code send_code,
i.ATTRIBUTE10 total_code,
f.pay_mode,
j.type_name as policy_type_name,
e.Internal_Id AS product_code,
round(a.distri_amount, 2) AS fee_amount,
decode(a.posted,
'Y',
to_char(i.transaction_date, 'yyyy-mm-dd'),
to_char(a.distri_date, 'yyyy-mm-dd')) As finish_time,
F.DR_SEG1,
F.DR_SEG2,
F.DR_SEG3,
F.DR_SEG4,
F.DR_SEG5,
F.DR_SEG6,
f.dr_seg7,
f.dr_seg8,
f.dr_seg9,
f.dr_seg10,
f.cr_seg1,
f.cr_seg2,
f.cr_seg3,
f.cr_seg4,
f.cr_seg5,
f.cr_seg6,
f.cr_seg7,
f.cr_seg8,
f.cr_seg9,
f.cr_seg10,
f.je_posting_id as cred_id
FROM T_CAPITAL_DISTRIBUTE a,
t_contract_master b,
t_channel_type j,
t_company_organ c,
t_capital_distri_type d,
t_product_life e,
t_contract_product f,
(select * from T_BIZ_ACCOUNTING_INFO where DATA_TABLE = '7') F,
T_GL_BIZ_INTERFACE i,
(select organ_id
from t_company_organ
start with organ_id = '101'
connect by parent_id = prior organ_id) o
WHERE a.policy_id = b.policy_id
and a.item_id = f.item_id(+)
AND b.organ_id = c.Organ_Id
AND a.distri_type = d.distri_type
AND a.product_id = e.product_id
and b.policy_type = j.INDIVIDUAL_GROUP
AND A.capital_id = F.FEE_ID(+)
AND A.cred_id = i.posting_id(+)
and a.organ_id = i.segment1(+)
and nvl(a.posted, 'N') = 'Y'
and a.cred_id = 493997
and i.transaction_date >= to_date('2011-11-01', 'yyyy-MM-dd')
and i.transaction_date < to_date('2011-11-30', 'yyyy-MM-dd') + 1
and a.distri_type = i.reference3(+)
and i.segment1 = o.organ_id(+);
盖尔说这个SQL逻辑读有2千万,跑300s,返回9000条数据,SQL 执行计划如下:
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 356 | 27 (0)|
| 1 | SORT UNIQUE | | 1 | 356 | 27 (0)|
|* 2 | HASH JOIN OUTER | | 1 | 356 | 12 (9)|
| 3 | NESTED LOOPS | | 1 | 350 | 10 (10)|
| 4 | NESTED LOOPS | | 1 | 338 | 9 (12)|
| 5 | NESTED LOOPS OUTER | | 1 | 302 | 8 (13)|
| 6 | NESTED LOOPS | | 1 | 171 | 7 (15)|
| 7 | NESTED LOOPS | | 1 | 125 | 6 (17)|
| 8 | NESTED LOOPS | | 1 | 100 | 5 (20)|
| 9 | NESTED LOOPS OUTER | | 1 | 86 | 4 (25)|
| 10 | NESTED LOOPS | | 1 | 76 | 3 (34)|
| 11 | TABLE ACCESS BY INDEX ROWID| T_GL_BIZ_INTERFACE | 1 | 24 | 2 (50)|
|* 12 | INDEX SKIP SCAN | IDX10 | 1 | | 3 (0)|
|* 13 | TABLE ACCESS BY INDEX ROWID| T_CAPITAL_DISTRIBUTE | 1 | 52 | 2 (50)|
|* 14 | INDEX RANGE SCAN | IDX_CAPITAL_DISTR__CRED_ORGAN | 15 | | 2 (0)|
| 15 | TABLE ACCESS BY INDEX ROWID | T_CONTRACT_PRODUCT | 1 | 10 | 2 (50)|
|* 16 | INDEX UNIQUE SCAN | PK_T_CONTRACT_PRODUCT | 1 | | 1 (0)|
| 17 | TABLE ACCESS BY INDEX ROWID | T_PRODUCT_LIFE | 1 | 14 | 2 (50)|
|* 18 | INDEX UNIQUE SCAN | PK_T_PRODUCT_LIFE | 1 | | |
| 19 | TABLE ACCESS BY INDEX ROWID | T_CAPITAL_DISTRI_TYPE | 1 | 25 | 2 (50)|
|* 20 | INDEX UNIQUE SCAN | PK_T_CAPITAL_DISTRI_TYPE | 1 | | |
| 21 | TABLE ACCESS BY INDEX ROWID | T_CONTRACT_MASTER | 1 | 46 | 2 (50)|
|* 22 | INDEX UNIQUE SCAN | PK_T_CONTRACT_MASTER | 1 | | 1 (0)|
| 23 | TABLE ACCESS BY INDEX ROWID | T_BIZ_ACCOUNTING_INFO | 1 | 131 | 2 (50)|
|* 24 | INDEX RANGE SCAN | IDX_BIZ_ACCOUNTING_INFO__FEE_ | 1 | | 2 (0)|
| 25 | TABLE ACCESS BY INDEX ROWID | T_COMPANY_ORGAN | 1 | 36 | 2 (50)|
|* 26 | INDEX UNIQUE SCAN | PK_T_COMPANY_ORGAN | 1 | | |
| 27 | TABLE ACCESS BY INDEX ROWID | T_CHANNEL_TYPE | 1 | 12 | 2 (50)|
|* 28 | INDEX UNIQUE SCAN | PK_T_CHANNEL_TYPE | 1 | | |
| 29 | VIEW | | 7 | 42 | |
|* 30 | CONNECT BY WITH FILTERING | | | | |
| 31 | NESTED LOOPS | | | | |
|* 32 | INDEX UNIQUE SCAN | PK_T_COMPANY_ORGAN | 1 | 6 | |
| 33 | TABLE ACCESS BY USER ROWID | T_COMPANY_ORGAN | | | |
| 34 | NESTED LOOPS | | | | |
| 35 | BUFFER SORT | | 7 | 70 | |
| 36 | CONNECT BY PUMP | | | | |
|* 37 | INDEX RANGE SCAN | T_COMPANY_ORGAN_IDX_002 | 7 | 70 | 1 (0)|
-------------------------------------------------------------------------------------------------------------
2 - access("I"."SEGMENT1"="O"."ORGAN_ID"(+))
12 - access("I"."TRANSACTION_DATE">=TO_DATE('2011-11-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss') AND
"I"."POSTING_ID"=493997 AND "I"."TRANSACTION_DATE"<TO_DATE('2011-12-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss'))
filter("I"."POSTING_ID"=493997)
13 - filter("A"."DISTRI_TYPE"="I"."REFERENCE3")
14 - access("A"."CRED_ID"=493997 AND "A"."ORGAN_ID"="I"."SEGMENT1")
filter(NVL("A"."POSTED",'N')='Y')
16 - access("A"."ITEM_ID"="F"."ITEM_ID"(+))
18 - access("A"."PRODUCT_ID"="E"."PRODUCT_ID")
20 - access("A"."DISTRI_TYPE"="D"."DISTRI_TYPE")
22 - access("A"."POLICY_ID"="B"."POLICY_ID")
24 - access("A"."CAPITAL_ID"="T_BIZ_ACCOUNTING_INFO"."FEE_ID"(+) AND
"T_BIZ_ACCOUNTING_INFO"."DATA_TABLE"(+)=7)
26 - access("B"."ORGAN_ID"="C"."ORGAN_ID")
28 - access("B"."POLICY_TYPE"="J"."INDIVIDUAL_GROUP")
30 - filter("T_COMPANY_ORGAN"."ORGAN_ID"='101')
32 - access("T_COMPANY_ORGAN"."ORGAN_ID"='101')
37 - access("T_COMPANY_ORGAN"."PARENT_ID"=NULL)
65 rows selected.
从执行计划上去看,这个SQL基本上没有技术上可以进一步优化的地方了,你可能会说这里不应该走INDEX SKIP SCAN ,应该走INDEX RANGE SCAN
但是这个都是小问题,它不是决定性因素,SQL 优化从技术上 不可行之后,就应该立马分析业务,请仔细观察这个SQL
它有很多的外连接,外连接很特殊,因为外连接的驱动表的顺序是固定的 比如
a left join b 那么 a就只能做驱动表(不管是走nested loops outer 或者hash join outer) 你没办法更改驱动表的顺序,哪怕你用leading ,order hint都不行
正是因为这个SQL里面有很多外连接,SQL的访问顺序给固定死了,所以没办法从技术上调优SQL了
我让盖尔把外连接的(+) 去掉,跑一下SQL ,SQL只需要30秒就能跑完 ,执行计划如下
----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 354 | 27 (0)|
| 1 | SORT UNIQUE | | 1 | 354 | 27 (0)|
| 2 | TABLE ACCESS BY INDEX ROWID | T_BIZ_ACCOUNTING_INFO | 1 | 131 | 2 (50)|
| 3 | NESTED LOOPS | | 1 | 354 | 12 (9)|
| 4 | NESTED LOOPS | | 1 | 223 | 11 (10)|
| 5 | NESTED LOOPS | | 1 | 209 | 10 (10)|
| 6 | NESTED LOOPS | | 1 | 199 | 9 (12)|
| 7 | NESTED LOOPS | | 1 | 174 | 8 (13)|
| 8 | NESTED LOOPS | | 1 | 138 | 7 (15)|
| 9 | NESTED LOOPS | | 1 | 126 | 6 (17)|
|* 10 | HASH JOIN | | 1 | 80 | 5 (20)|
| 11 | TABLE ACCESS BY INDEX ROWID | T_CAPITAL_DISTRIBUTE | 1 | 50 | 2 (50)|
| 12 | NESTED LOOPS | | 1 | 74 | 3 (34)|
| 13 | TABLE ACCESS BY INDEX ROWID | T_GL_BIZ_INTERFACE | 1 | 24 | 2 (50)|
|* 14 | INDEX SKIP SCAN | IDX10 | 1 | | 3 (0)|
|* 15 | INDEX RANGE SCAN | IDX14 | 1 | | 2 (0)|
| 16 | VIEW | | 7 | 42 | |
|* 17 | FILTER | | | | |
|* 18 | CONNECT BY WITH FILTERING | | | | |
| 19 | NESTED LOOPS | | | | |
|* 20 | INDEX UNIQUE SCAN | PK_T_COMPANY_ORGAN | 1 | 6 | |
| 21 | TABLE ACCESS BY USER ROWID| T_COMPANY_ORGAN | | | |
| 22 | NESTED LOOPS | | | | |
| 23 | BUFFER SORT | | 7 | 70 | |
| 24 | CONNECT BY PUMP | | | | |
|* 25 | INDEX RANGE SCAN | T_COMPANY_ORGAN_IDX_002 | 7 | 70 | 1 (0)|
| 26 | TABLE ACCESS BY INDEX ROWID | T_CONTRACT_MASTER | 1 | 46 | 2 (50)|
|* 27 | INDEX UNIQUE SCAN | PK_T_CONTRACT_MASTER | 1 | | 1 (0)|
| 28 | TABLE ACCESS BY INDEX ROWID | T_CHANNEL_TYPE | 1 | 12 | 2 (50)|
|* 29 | INDEX UNIQUE SCAN | PK_T_CHANNEL_TYPE | 1 | | |
| 30 | TABLE ACCESS BY INDEX ROWID | T_COMPANY_ORGAN | 1 | 36 | 2 (50)|
|* 31 | INDEX UNIQUE SCAN | PK_T_COMPANY_ORGAN | 1 | | |
| 32 | TABLE ACCESS BY INDEX ROWID | T_CAPITAL_DISTRI_TYPE | 1 | 25 | 2 (50)|
|* 33 | INDEX UNIQUE SCAN | PK_T_CAPITAL_DISTRI_TYPE | 1 | | |
| 34 | TABLE ACCESS BY INDEX ROWID | T_CONTRACT_PRODUCT | 1 | 10 | 2 (50)|
|* 35 | INDEX UNIQUE SCAN | PK_T_CONTRACT_PRODUCT | 1 | | 1 (0)|
| 36 | TABLE ACCESS BY INDEX ROWID | T_PRODUCT_LIFE | 1 | 14 | 2 (50)|
|* 37 | INDEX UNIQUE SCAN | PK_T_PRODUCT_LIFE | 1 | | |
|* 38 | INDEX RANGE SCAN | IDX_BIZ_ACCOUNTING_INFO__FEE_ | 1 | | 2 (0)|
----------------------------------------------------------------------------------------------------------------
10 - access("I"."SEGMENT1"="O"."ORGAN_ID")
14 - access("I"."TRANSACTION_DATE">=TO_DATE('2011-11-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss') AND
"I"."POSTING_ID"=493997 AND "I"."TRANSACTION_DATE"<TO_DATE('2011-12-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss'))
filter("I"."POSTING_ID"=493997)
15 - access("A"."CRED_ID"=493997 AND "A"."ORGAN_ID"="I"."SEGMENT1" AND "A"."DISTRI_TYPE"="I"."REFERENCE3")
filter(NVL("A"."POSTED",'N')='Y' AND "A"."DISTRI_TYPE"="I"."REFERENCE3")
17 - filter(TO_DATE('2011-11-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')<TO_DATE('2011-12-01 00:00:00',
'yyyy-mm-dd hh24:mi:ss') AND TO_DATE('2011-12-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')>TO_DATE('2011-11-01
00:00:00', 'yyyy-mm-dd hh24:mi:ss'))
18 - filter("T_COMPANY_ORGAN"."ORGAN_ID"='101')
20 - access("T_COMPANY_ORGAN"."ORGAN_ID"='101')
25 - access("T_COMPANY_ORGAN"."PARENT_ID"=NULL)
27 - access("A"."POLICY_ID"="B"."POLICY_ID")
29 - access("B"."POLICY_TYPE"="J"."INDIVIDUAL_GROUP")
31 - access("B"."ORGAN_ID"="C"."ORGAN_ID")
33 - access("A"."DISTRI_TYPE"="D"."DISTRI_TYPE")
35 - access("A"."ITEM_ID"="F"."ITEM_ID")
37 - access("A"."PRODUCT_ID"="E"."PRODUCT_ID")
38 - access("A"."CAPITAL_ID"="T_BIZ_ACCOUNTING_INFO"."FEE_ID" AND "T_BIZ_ACCOUNTING_INFO"."DATA_TABLE"=7)
所以系统设计的时候,应该尽量避免出现 left outer join, right outer join ,尤其是大表,大表更应该尽量避它作为外连接的驱动表