我的生产环境中有以下查询
SELECT CIF,
DECODE( 'INFENG' , '' ,NVL(ALT1_NAME,NAME),NAME),
DECODE( 'INFENG' , '' ,NVL(ALT1_SHORT_NAME,SHORT_NAME),SHORT_NAME),
CORP_ID
FROM tb.CMG ,
tb.SST
WHERE SST.BANK_ID = '54'
AND CMG.PRIMARY_SOL_ID = SST.SOL_ID
AND SST.SET_ID = '000'
AND CMG.BANK_ID = '54'
AND CMG.ENTITY_CRE_FLG = 'Y'
AND EXISTS
(SELECT 1
FROM tb.CPHONE
WHERE CPHONE.PHONE_B2KID = CMG.CIF_ID
AND PHONENO LIKE '%4444%'
)
AND CORP_ID IS NULL
AND ROWNUM < 801
ORDER BY 4
结果需要5分钟以上。查询的解释计划是
Plan hash value: 4143484456
--------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 207 | 9732 (1)| 00:00:01 |
| 1 | SORT ORDER BY | | 1 | 207 | 9732 (1)| 00:00:01 |
|* 2 | COUNT STOPKEY | | | | | |
|* 3 | FILTER | | | | | |
| 4 | NESTED LOOPS | | 4325 | 874K| 5297 (1)| 00:00:01 |
| 5 | NESTED LOOPS | | 44112 | 874K| 5297 (1)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | IDX_SOL_ID_SET_TABLE | 1 | 16 | 1 (0)| 00:00:01 |
|* 7 | INDEX RANGE SCAN | IX_ACCOUNTS_PSOLID | 44112 | | 1 (0)| 00:00:01 |
|* 8 | TABLE ACCESS BY INDEX ROWID | ACCOUNTS | 4325 | 806K| 5296 (1)| 00:00:01 |
| 9 | NESTED LOOPS SEMI NA | | 1 | 92 | 2 (0)| 00:00:01 |
|* 10 | TABLE ACCESS BY INDEX ROWID BATCHED| PHONEEMAIL | 1 | 83 | 1 (0)| 00:00:01 |
|* 11 | INDEX RANGE SCAN | IX_ORGKEY_PHONEEMAILTYPE | 3 | | 1 (0)| 00:00:01 |
|* 12 | TABLE ACCESS BY INDEX ROWID BATCHED| NON_CUSTOMERS | 1 | 9 | 1 (0)| 00:00:01 |
|* 13 | INDEX RANGE SCAN | PK322 | 1 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(ROWNUM<801)
3 - filter( EXISTS (SELECT /*+ <not feasible>)
6 - access("SST"."SET_ID"='000' AND "SST"."BANK_ID"='54')
filter("SST"."BANK_ID"='54')
7 - access("ACCOUNTS"."PRIMARY_SOL_ID"=SYS_OP_C2C("SOL_ID") AND "ACCOUNTS"."BANK_ID"=U'54')
8 - filter("ACCOUNTS"."ENTITY_CRE_FLAG"=U'Y' AND "ACCOUNTS"."CORP_ID" IS NULL)
10 - filter("PHONEEMAIL"."PHONEOREMAIL"=U'PHONE' AND SUBSTR("PHONENO",1,20) LIKE U'%4444%' AND
SUBSTR("PHONENO",1,20) IS NOT NULL AND "SUSPECTID" IS NULL AND "CONTACTID" IS NULL)
11 - access("PHONEEMAIL"."ORGKEY"=:B1)
12 - filter("A"."CONVFLAG"=U'N' OR "A"."CONVFLAG" IS NULL)
13 - access("A"."NONCUSTOMERID"="PHONEEMAIL"."NONCUSTOMERID")
Note
-----
- dynamic statistics used: dynamic sampling (level=1)
- this is an adaptive plan
- 1 Sql Plan Directive used for this statement
有人可以告诉我如何优化此查询以减少执行时间。
我在测试数据库中的日期相同,查询时间不到一分钟。那里的解释计划是
Plan hash value: 888087711
----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 296 | 15491 (7)| 00:00:01 |
| 1 | SORT UNIQUE | | 1 | 296 | 15490 (7)| 00:00:01 |
|* 2 | COUNT STOPKEY | | | | | |
| 3 | NESTED LOOPS | | 1 | 296 | 15489 (7)| 00:00:01 |
| 4 | NESTED LOOPS | | 1 | 283 | 15488 (7)| 00:00:01 |
|* 5 | HASH JOIN RIGHT SEMI NA | | 1 | 92 | 15487 (7)| 00:00:01 |
|* 6 | TABLE ACCESS STORAGE FULL | NON_CUSTOMERS | 9 | 81 | 2 (0)| 00:00:01 |
|* 7 | TABLE ACCESS STORAGE FULL | PHONEEMAIL | 783K| 62M| 15482 (7)| 00:00:01 |
|* 8 | TABLE ACCESS BY INDEX ROWID BATCHED| ACCOUNTS | 1 | 191 | 1 (0)| 00:00:01 |
|* 9 | INDEX RANGE SCAN | IX_ACCOUNTS_ORGKEY | 1 | | 1 (0)| 00:00:01 |
|* 10 | INDEX RANGE SCAN | IDX_SOL_ID_SET_TABLE | 1 | 13 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(ROWNUM<801)
5 - access("A"."NONCUSTOMERID"="PHONEEMAIL"."NONCUSTOMERID")
6 - storage("A"."CONVFLAG"=U'N' OR "A"."CONVFLAG" IS NULL)
filter("A"."CONVFLAG"=U'N' OR "A"."CONVFLAG" IS NULL)
7 - storage("PHONEEMAIL"."PHONEOREMAIL"=U'PHONE')
filter("PHONEEMAIL"."PHONEOREMAIL"=U'PHONE' AND SUBSTR("PHONENO",1,20) LIKE U'%4444%' AND
SUBSTR("PHONENO",1,20) IS NOT NULL AND "SUSPECTID" IS NULL AND "CONTACTID" IS NULL)
8 - filter("ACCOUNTS"."ENTITY_CRE_FLAG"=U'Y' AND "ACCOUNTS"."CORP_ID" IS NULL)
9 - access("PHONEEMAIL"."ORGKEY"="ACCOUNTS"."ORGKEY" AND "ACCOUNTS"."BANK_ID"=U'54')
10 - access("SST"."SET_ID"='000' AND "SST"."BANK_ID"='54')
filter("SST"."BANK_ID"='54' AND "ACCOUNTS"."PRIMARY_SOL_ID"=SYS_OP_C2C("SST"."SOL_ID"))
Note
-----
- dynamic statistics used: dynamic sampling (level=1)
- this is an adaptive plan
- 2 Sql Plan Directives used for this statement
答案 0 :(得分:0)
您没有提供太多信息(例如,如果查询返回完整的800行或更少,则没有说明,所以无法准确回答。
以下是解决声明问题时应遵循的一些步骤:
<强> 1。了解查询正在做什么
该查询正在加入两个表CMG
和SST
。另外,它会从EXISTS
到CMG
执行CPHOME
子查询
(这可能是两张桌子的视图)。
最后,您将结果限制为800行。
<强> 2。找到可接受的执行计划
你应该说服自己哪个执行计划是正确的 - 知道你的数据需要知道。
明显的起点是表CMG
(因为它参与了连接和EXISTS子查询)。
问题是接下来该做什么 - 加入或EXISTS条件。您的第一个执行计划首先执行连接,然后解析EXISTS(NESTED LOOPS SEMI
)。
漫长的执行时间表明它不行。一种解释是EXISTS非常严格,即CPHONE
存在的CMG
不多。
因此,您只能通过它们执行大量的连接循环到SST表,因为不存在CPHONE
。
第二个执行计划支持这个理论,因为它首先执行EXISTS谓词(使用HASH JOIN RIGHT SEMI
)而不是连接。
第3。让Oracle执行该执行计划
那么甲骨文为什么不做“正确”的工作呢?书架上写着它 - 这里只有非常基本和明显的东西:
dynamic sampling level 1
中使用,这意味着至少有一个表没有优化器统计信息和您只使用32个块进行动态采样。收集丢失的统计信息或者增加DS级别(您可以在查询中使用提示)。
请注意,您在两个环境Sql Plan指令中使用,并且您在测试中有两个,而在prod中只有一个。检查他们试图纠正错误估计的哪一部分。
使用dbms_sqltune.report_sql_monitor
运行查询,以查看估算的基数与广告之间的差异
实际基数。