开发说下面这个sql语句跑不出结果
SELECT t1.order_id, t2.order_name, t1.order_flow_no, t1.order_type, t1.agent_id, t1.money, t1.order_create_time FROM (SELECT re.id AS order_id, re.serialnumber AS order_flow_no, re.money AS money, '1' AS order_type, re.agent_id AS agent_id, re.create_date AS order_create_time FROM tb_recharge re WHERE create_date >= to_date('2014-07-08 00:00:00', 'yyyy-mm-dd hh24:mi:ss') - INTERVAL '1' DAY AND create_date < to_date('2014-07-09 00:00:00', 'yyyy-mm-dd hh24:mi:ss') AND re.status != 2) t1 LEFT JOIN (SELECT tt1.orderform_flow_no, tt1.order_name FROM (SELECT tp1.PAYMENT_FLOW_NO, tp1.orderform_flow_no, tp1.orderform_name AS order_name FROM tb_payment tp1 WHERE create_time >= to_date('2014-07-08 00:00:00', 'yyyy-mm-dd hh24:mi:ss') - INTERVAL '1' DAY AND create_time < to_date('2014-07-09 00:00:00', 'yyyy-mm-dd hh24:mi:ss') AND tp1.orderform_type = 1 AND tp1.id NOT IN (SELECT tr.payment_id FROM tb_refund tr)) tt1 LEFT JOIN (SELECT business_flow_no FROM tb_fund_flow f WHERE f.business_type = 0 AND create_time >= to_date('2014-07-08 00:00:00', 'yyyy-mm-dd hh24:mi:ss') - INTERVAL '1' DAY AND create_time < to_date('2014-07-09 00:00:00', 'yyyy-mm-dd hh24:mi:ss')) tt2 ON tt1.PAYMENT_FLOW_NO = tt2.business_flow_no WHERE tt2.business_flow_no IS NOT NULL) t2 ON t1.order_flow_no = t2.orderform_flow_no WHERE t2.orderform_flow_no IS NOT NULL
plan如下
--------------------------------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |--------------------------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 440 | 158K (75)| 00:31:40 | | ||* 1 | HASH JOIN ANTI | | 1 | 440 | 158K (75)| 00:31:40 | | || 2 | NESTED LOOPS | | 1 | 434 | 156K (75)| 00:31:23 | | || 3 | NESTED LOOPS | | 1 | 434 | 156K (75)| 00:31:23 | | || 4 | MERGE JOIN CARTESIAN | | 1 | 125 | 156K (75)| 00:31:23 | | || 5 | PARTITION RANGE SINGLE | | 1 | 83 | 0 (0)| 00:00:01 | 12 | 12 ||* 6 | TABLE ACCESS BY LOCAL INDEX ROWID| TB_RECHARGE | 1 | 83 | 0 (0)| 00:00:01 | 12 | 12 ||* 7 | INDEX RANGE SCAN | TB_RECHARGE_I5 | 1 | | 0 (0)| 00:00:01 | 12 | 12 || 8 | BUFFER SORT | | 673K| 26M| 156K (75)| 00:31:23 | | || 9 | PARTITION RANGE SINGLE | | 673K| 26M| 156K (75)| 00:31:23 | 8 | 8 ||* 10 | TABLE ACCESS FULL | TB_FUND_FLOW | 673K| 26M| 156K (75)| 00:31:23 | 8 | 8 ||* 11 | INDEX UNIQUE SCAN | UNIQ_FLOW_TYPE1 | 1 | | 2 (0)| 00:00:01 | | ||* 12 | TABLE ACCESS BY GLOBAL INDEX ROWID | TB_PAYMENT | 1 | 309 | 2 (0)| 00:00:01 | 8 | 8 || 13 | TABLE ACCESS FULL | TB_REFUND | 150K| 884K| 1402 (1)| 00:00:17 | | |--------------------------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):--------------------------------------------------- 1 - access("TP1"."ID"="TR"."PAYMENT_ID") 6 - filter("RE"."STATUS"<>2) 7 - access("CREATE_DATE">=TO_DATE(' 2014-07-07 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND "CREATE_DATE"=TO_DATE(' 2014-07-07 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND "CREATE_TIME" =TO_DATE(' 2014-07-07 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND "CREATE_TIME"
相关表大小说明:
tb_recharge: 6G
tb_fund_flow: 9G
tb_payment: 4G
都为分区表
这个执行计划一眼就可以看出很多表统计信息有问题,先不管统计信息是否正确,定位到id为4 中tb_recharge 和tb_fund_folw 两个大表做MERGE JOIN CARTESIAN 笛卡尔,并且tb_found_flow TABLE FULL SCAN, 发现问题的时候这个sql已经跑了3个小时了。
注意上面sql语句的写法,都是将大表先过滤在相互去join,说明一点这个sql最终join结果只有2条数据。但是最终CBO查询改写了,导致内敛视图合并,出现笛卡尔。看到这里优化就非常简单了,于是我加了一个hint no_merge不允许视图合并。
下面是最后的结果,1s数据就出来了
SELECT /*+ no_merge(t1) no_merge(t2)*/ t1.order_id, t2.order_name, t1.order_flow_no, t1.order_type, t1.agent_id, t1.money, t1.order_create_time FROM (SELECT re.id AS order_id, re.serialnumber AS order_flow_no, re.money AS money, '1' AS order_type, re.agent_id AS agent_id, re.create_date AS order_create_time FROM tb_recharge re WHERE create_date >= to_date('2014-07-08 00:00:00', 'yyyy-mm-dd hh24:mi:ss') - INTERVAL '1' DAY AND create_date < to_date('2014-07-09 00:00:00', 'yyyy-mm-dd hh24:mi:ss') AND re.status != 2) t1 LEFT JOIN (SELECT tt1.orderform_flow_no, tt1.order_name FROM (SELECT tp1.PAYMENT_FLOW_NO, tp1.orderform_flow_no, tp1.orderform_name AS order_name FROM tb_payment tp1 WHERE create_time >= to_date('2014-07-08 00:00:00', 'yyyy-mm-dd hh24:mi:ss') - INTERVAL '1' DAY AND create_time < to_date('2014-07-09 00:00:00', 'yyyy-mm-dd hh24:mi:ss') AND tp1.orderform_type = 1 AND tp1.id NOT IN (SELECT tr.payment_id FROM tb_refund tr)) tt1 LEFT JOIN (SELECT /*+ index(f IDX_FUNDFLOW_CRTTIME) */ business_flow_no FROM tb_fund_flow f WHERE f.business_type = 0 AND create_time >= to_date('2014-07-08 00:00:00', 'yyyy-mm-dd hh24:mi:ss') - INTERVAL '1' DAY AND create_time < to_date('2014-07-09 00:00:00', 'yyyy-mm-dd hh24:mi:ss')) tt2 ON tt1.PAYMENT_FLOW_NO = tt2.business_flow_no WHERE tt2.business_flow_no IS NOT NULL) t2 ON t1.order_flow_no = t2.orderform_flow_no WHERE t2.orderform_flow_no IS NOT NULL
注意由于tb_fund_flow 是按月分区表,这里查询一天的数据,必然走index比较快,可见统计信息错误,由于系统所有sql语句都在1秒内出结果,我没有去收集统计信息,于是加了这个hint /*+ index(f IDX_FUNDFLOW_CRTTIME) */ ,下面是最终的执行计划
---------------------------------------------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop |---------------------------------------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 327 | | 415K (1)| 01:23:07 | | ||* 1 | HASH JOIN | | 1 | 327 | | 415K (1)| 01:23:07 | | || 2 | PARTITION RANGE SINGLE | | 1 | 73 | | 0 (0)| 00:00:01 | 12 | 12 || 3 | VIEW | | 1 | 73 | | 0 (0)| 00:00:01 | | ||* 4 | TABLE ACCESS BY LOCAL INDEX ROWID | TB_RECHARGE | 1 | 83 | | 0 (0)| 00:00:01 | 12 | 12 ||* 5 | INDEX RANGE SCAN | TB_RECHARGE_I5 | 1 | | | 0 (0)| 00:00:01 | 12 | 12 || 6 | VIEW | | 6740 | 1671K| | 415K (1)| 01:23:07 | | ||* 7 | HASH JOIN | | 6740 | 2349K| | 415K (1)| 01:23:07 | | ||* 8 | HASH JOIN RIGHT ANTI | | 990 | 304K| 2656K| 41160 (1)| 00:08:14 | | || 9 | TABLE ACCESS FULL | TB_REFUND | 150K| 884K| | 1402 (1)| 00:00:17 | | || 10 | PARTITION RANGE SINGLE | | 98977 | 29M| | 38125 (1)| 00:07:38 | 8 | 8 ||* 11 | TABLE ACCESS BY LOCAL INDEX ROWID| TB_PAYMENT | 98977 | 29M| | 38125 (1)| 00:07:38 | 8 | 8 ||* 12 | INDEX RANGE SCAN | IDX_PAYMENT_CRTTIME | 98977 | | | 1550 (1)| 00:00:19 | 8 | 8 || 13 | PARTITION RANGE SINGLE | | 673K| 26M| | 374K (1)| 01:14:53 | 8 | 8 ||* 14 | TABLE ACCESS BY LOCAL INDEX ROWID | TB_FUND_FLOW | 673K| 26M| | 374K (1)| 01:14:53 | 8 | 8 ||* 15 | INDEX RANGE SCAN | IDX_FUNDFLOW_CRTTIME | 697K| | | 12504 (1)| 00:02:31 | 8 | 8 |---------------------------------------------------------------------------------------------------------------------------------------
添加了no_merge Hint后可以看到id等于3和6出现了view关键字,达到了最后sql写法的目的,先过滤大数据在join,最后这个sql 1s出了结果。开发傻眼了