【问题标题】:Select query taking time in Oracle在 Oracle 中选择查询耗时
【发布时间】:2017-12-20 09:00:36
【问题描述】:

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

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

【问题讨论】:

  • 这对于 dba 交换会更好:dba.stackexchange.com
  • CMG.PRIMARY_SOL_IDSST.SOL_ID 列的数据类型是什么。希望其中之一是NVARCHAR(),导致隐式转换调用SYS_OP_C2C(),抑制索引的使用。
  • @MaheswaranRavisankar cmg.primary_sol_id 是 NVARCHAR2 而 sst.sol_id 是 VARCHAR2
  • 太好了,这可能是罪魁祸首,您可能想在该列上添加一个基于函数的索引,例如to_nchar(CMG.PRIMARY_SOL_ID)。或者匹配这些列的数据类型。 More details here
  • 解释计划显示总执行时间(估计)为一秒。那是怎么回事?你能生成一个实际的执行计划并分享吗?然后 - 如果优化器认为它会在一秒钟内完成所有事情,那么您的统计数据可能已经过时了。他们是吗?而且,为什么解释计划显示 SELECT 语句将只返回一行而不是 800? ...您发布的查询中的计划不是真的吗?太多东西看起来不一样了。

标签: oracle database-performance oracle12c


【解决方案1】:

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

您应该按照以下步骤对语句进行故障排除:

1.了解查询在做什么

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

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

您应该说服自己哪个执行计划是正确的 - 需要对您的数据有一些基本的了解。

明显的起点是表CMG(因为它同时涉及连接和EXISTS 子查询)。

问题是接下来要做什么 - 连接或 EXISTS 条件。您的第一个执行计划首先执行连接,然后解决 EXISTS (NESTED LOOPS SEMI)。 长的执行时间表明它是不行的。一种解释是 EXISTS 非常严格,即 CMG 不存在很多 CPHONEs。 因此,您对 SST 表执行了很多连接循环,只是为了通过它们离开,因为不存在 CPHONE

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

3。让 Oracle 执行该执行计划

那么为什么甲骨文没有做“正确”的工作?书架上都写着它——这里只有非常基本和显而易见的东西:

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

要么收集缺失的统计信息,要么增加 DS 级别(您可以通过查询中的提示来完成)。

  • 请注意,您在两种环境中都使用了 Sql Plan 指令,并且您有两个在测试中,只有一个在生产中。检查他们试图纠正错误估计的哪一部分。

  • 使用dbms_sqltune.report_sql_monitor 运行查询以查看估计的基数和 实际基数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-27
    • 1970-01-01
    • 2016-10-08
    • 2010-09-23
    • 1970-01-01
    • 2016-11-14
    相关资源
    最近更新 更多