使用/*+ no_expand */避免执行路径中的CONCATENATION
昨天的一次SQL优化过程,仅作记录。
背景是在一台11gR2的机器上,用户反映一个查询操作非常慢,基本都要20~30分钟才能有结果。经过仔细查看该SQL的执行计划,发现由于SQL中使用了or,导致CBO走出了一个非常糟糕的CONCATENATION路径。因为SQL本身是通过程序动态生成的,开发人员不愿意进行大的调整,就采用了/*+ no_expand */提升的方式避免CBO选择CONCATENATION方式,调整之后执行时间提升到1分钟之内。
no_expand提示的说明是
The NO_EXPAND hint prevents the cost-based optimizer from considering OR-expansion for queries having OR conditions or IN-lists in the WHERE clause. Usually, the optimizer considers using OR expansion and uses this method if it decides that the cost is lower than not using it.
use_concat提示的说明是
The USE_CONCAT hint forces combined OR conditions in the WHERE clause of a query to be transformed into a compound query using the UNION ALL set operator. Generally, this transformation occurs only if the cost of the query using the concatenations is cheaper than the cost without them.
问题解决了,不过留下了一个疑点:在Oracle 11gR2中,在cost明显很高的情况下,CBO是不应该选用CONCATENATION的,在 wait4friend 的测试过程中,使用SQL*Plus或者GUI的开发工具执行该SQL,都不会生成CONCATENATION的执行计划,为了重现这个问题,必须使用/*+ use_concat */来模拟。那么问题就奇怪了,为什么通过程序提交过来的SQL总是会选择很差的计划?到底是什么因素导致CBO做出错误的选择,目前还没有找到原因。一个可能的原因是,开发人员使用的是ojdbc14.jar的驱动,不过尚未进行验证。
语句如下。红色部分是后面修改的,为了避免CBO选择CONCATENATION路径,使用了/*+ no_expand */提示。
SELECT COUNT (*) FROM (SELECT cu.customer_id, bi.bill_id FROM (SELECT /*+ no_expand */ cu.customer_id FROM ep_customer cu, ep_cus_expands ex, (SELECT id, project_id, name_alias FROM no_card_channel) ch WHERE 1 = 1 AND ex.create_time >= 20000101000000 AND ex.create_time <= 20121213235959 AND ex.TYPE = 'NC_CHANNEL' AND ex.validity = 'true' AND ( ex.ext_data = ch.project_id OR ex.ext_data = ch.name_alias) AND ex.customer_id = cu.customer_id) cu LEFT JOIN (SELECT b.bill_id, b.customer_id, b.amount, b.trans_id, c.settle_time FROM ep_bill b, ep_cup_certificate c WHERE b.status IN ('paid', 'void', 'refund') AND c.pay_class = 'NC_PAY' AND c.TYPE = 'purchase' AND c.validity = 'true' AND b.bill_id = c.bill_id UNION ALL SELECT b.bill_id, b.customer_id, b.amount, b.trans_id, '' settle_time FROM ep_bill b WHERE b.trans_id = '1382') bi ON cu.customer_id = bi.customer_id) WHERE bill_id IS NULL;
糟糕的执行计划
Plan hash value: 3646981028
---------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | | 120K(100)| |
| 1 | SORT AGGREGATE | | 1 | 83 | | | |
| 2 | CONCATENATION | | | | | | |
| 3 | HASH JOIN | | 517K| 40M| | 61936 (1)| 00:12:24 |
| 4 | TABLE ACCESS FULL | NO_CARD_CHANNEL | 130 | 3250 | | 3 (0)| 00:00:01 |
| 5 | FILTER | | | | | | |
| 6 | HASH JOIN RIGHT OUTER | | 653K| 36M| 1848K| 61929 (1)| 00:12:24 |
| 7 | VIEW | | 78526 | 920K| | 48749 (1)| 00:09:45 |
| 8 | UNION-ALL | | | | | | |
| 9 | HASH JOIN | | 74882 | 3436K| 2928K| 28390 (1)| 00:05:41 |
| 10 | TABLE ACCESS FULL | EP_CUP_CERTIFICATE | 74882 | 2047K| | 5369 (1)| 00:01:05 |
| 11 | TABLE ACCESS FULL | EP_BILL | 1702K| 30M| | 20366 (1)| 00:04:05 |
| 12 | TABLE ACCESS FULL | EP_BILL | 3644 | 58304 | | 20359 (1)| 00:04:05 |
| 13 | HASH JOIN | | 655K| 28M| 32M| 11287 (1)| 00:02:16 |
| 14 | TABLE ACCESS FULL | EP_CUS_EXPANDS | 655K| 24M| | 5595 (2)| 00:01:08 |
| 15 | INDEX FAST FULL SCAN| P_CUSTOMER_ID | 2654K| 15M| | 1791 (1)| 00:00:22 |
| 16 | HASH JOIN | | 1 | 83 | | 58518 (1)| 00:11:43 |
| 17 | MERGE JOIN CARTESIAN | | 1 | 43 | | 52919 (1)| 00:10:36 |
| 18 | FILTER | | | | | | |
| 19 | HASH JOIN RIGHT OUTER| | 1 | 18 | 1848K| 52916 (1)| 00:10:35 |
| 20 | VIEW | | 78526 | 920K| | 48749 (1)| 00:09:45 |
| 21 | UNION-ALL | | | | | | |
| 22 | HASH JOIN | | 74882 | 3436K| 2928K| 28390 (1)| 00:05:41 |
| 23 | TABLE ACCESS FULL| EP_CUP_CERTIFICATE | 74882 | 2047K| | 5369 (1)| 00:01:05 |
| 24 | TABLE ACCESS FULL| EP_BILL | 1702K| 30M| | 20366 (1)| 00:04:05 |
| 25 | TABLE ACCESS FULL | EP_BILL | 3644 | 58304 | | 20359 (1)| 00:04:05 |
| 26 | INDEX FAST FULL SCAN| P_CUSTOMER_ID | 2654K| 15M| | 1791 (1)| 00:00:22 |
| 27 | BUFFER SORT | | 130 | 3250 | | 52919 (1)| 00:10:36 |
| 28 | TABLE ACCESS FULL | NO_CARD_CHANNEL | 130 | 3250 | | 3 (0)| 00:00:01 |
| 29 | TABLE ACCESS FULL | EP_CUS_EXPANDS | 655K| 24M| | 5595 (2)| 00:01:08 |
---------------------------------------------------------------------------------------------------------
正常情况下的执行计划
Plan hash value: 3689968997
--------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | | 60823 (100)| |
| 1 | SORT AGGREGATE | | 1 | 97 | | | |
| 2 | NESTED LOOPS | | 4979 | 471K| | 60823 (1)| 00:12:10 |
| 3 | FILTER | | | | | | |
| 4 | HASH JOIN OUTER | | 3441 | 241K| | 56150 (1)| 00:11:14 |
| 5 | HASH JOIN | | 3452 | 155K| | 7399 (2)| 00:01:29 |
| 6 | TABLE ACCESS FULL | EP_CUS_EXPANDS | 3452 | 134K| | 5595 (2)| 00:01:08 |
| 7 | INDEX FAST FULL SCAN| P_CUSTOMER_ID | 2654K| 15M| | 1791 (1)| 00:00:22 |
| 8 | VIEW | | 78526 | 1993K| | 48749 (1)| 00:09:45 |
| 9 | UNION-ALL | | | | | | |
| 10 | HASH JOIN | | 74882 | 3436K| 2928K| 28390 (1)| 00:05:41 |
| 11 | TABLE ACCESS FULL | EP_CUP_CERTIFICATE | 74882 | 2047K| | 5369 (1)| 00:01:05 |
| 12 | TABLE ACCESS FULL | EP_BILL | 1702K| 30M| | 20366 (1)| 00:04:05 |
| 13 | TABLE ACCESS FULL | EP_BILL | 3644 | 58304 | | 20359 (1)| 00:04:05 |
| 14 | TABLE ACCESS FULL | NO_CARD_CHANNEL | 1 | 25 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------
posted on 2012-12-14 15:45 wait4friend 阅读(2515) 评论(0) 编辑 收藏 举报