在Oracle中选择查询花费时间

问题描述 投票:2回答:1

我在我的生产环境中有以下查询

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
oracle database-performance oracle12c
1个回答
0
投票

您没有提供太多信息(例如,您没有说明查询是否返回完整的800行或更少),因此无法得到确切的答案。

以下是解决该声明时应遵循的一些步骤:

1.了解查询正在做什么

该查询正在连接两个表CMGSST。另外它执行从EXISTSCMGCPHOME子查询(这可能是两个表的视图)。最后,您将结果限制为800行。

2.找到可接受的执行计划

您应该说服自己哪个执行计划是正确的 - 需要一些关于您的数据的基本知识。

显而易见的起点是表CMG(因为它涉及连接和EXISTS子查询)。

问题是下一步该做什么 - 加入或EXISTS条件。您的第一个执行计划首先执行连接,然后解析EXISTS(NESTED LOOPS SEMI)。漫长的执行时间表明它不行。一种解释是,EXISTS是非常严格的,即CPHONE不存在太多的CMGs。因此,您只能通过它们执行大量的连接循环到SST表,因为不存在CPHONE

第二个执行计划支持这个理论,因为它首先执行EXISTS谓词(使用HASH JOIN RIGHT SEMI)而不是连接。

3.让Oracle执行该执行计划

那么甲骨文为什么不做“正确”的工作呢?书架是关于它的 - 这里只有非常基本和明显的东西:

  • 你在两个环境dynamic sampling level 1中使用,这意味着至少有一个表根本没有优化器统计信息,你只使用32个块进行动态采样。

收集缺失的统计信息或者增加DS级别(您可以在查询中使用提示)。

  • 请注意,您在两个环境Sql Plan指令中使用,并且您在测试中有两个,在prod中只有一个。检查他们试图纠正错误估计的哪一部分。
  • 使用dbms_sqltune.report_sql_monitor运行查询,以查看估计的基数和实际基数之间的差异。
© www.soinside.com 2019 - 2024. All rights reserved.