Oracle解释计划解析——Oracle做全表访问

Posted

技术标签:

【中文标题】Oracle解释计划解析——Oracle做全表访问【英文标题】:Analysis of Oracle Explain Plan - Oracle Doing Full Table Access 【发布时间】:2017-06-15 18:37:44 【问题描述】:

我们有一个有问题的 SQL,在查看解释计划时,似乎有很多全表访问。据我了解,这是正常的,特别是如果查询更多位于计划内部而不是位于顶部。

每个表都有索引,并且使用了适当的列,因此不清楚 Oracle 为何不使用索引。任何人都可以就我是否应该期待如此多的全表扫描提供高级指导,或者这个 SQL 是否需要更好的优化?我们经常在 Oracle 中耗尽内存,删除 SORT 确实有助于降低成本,但对内存没有帮助。

SQL

SELECT
     TRNLOAD.WORK_ORDER_NUMBER,
     TRNMANIFESTHDR.CUST_MANIFEST_NUMBER,
     TRNMANIFESTHDR.STATE_MANIFEST_NUMBER,
     TRNINVENTORY.MAN_PAGE_NO,
     ...snipped columns for size
     TRNINVENTORY.MAN_SYS_NUMBER,
     NVL(REFINVSPLITCODES.DOUBLE_COUNT,'N') double_count,
     MONTHS_BETWEEN(SYSDATE,NVL(TRNLOAD.CHECKIN_TIME, TRNINVENTORY.GROUPED_DATE)) months,
     AESOP.DF_GETINVCOLORCODE 
        (TRNINVENTORY.INV_STATUS,
         TRNLOAD.CHECKOUT_TIME,
         DF_IS_ONSITECUST(TRNMANIFESTHDR.LOC_CODE,TRNMANIFESTHDR.BILLING_CUSTOMER))
              color_cd,
     TRNINVENTORY.CREATED_BY,
     TRNMANIFESTDETAIL.EPA_CONSENT_NUMBER 
FROM REFGENERATOR,
     TRNINVENTORY,
     TRNLOAD,
     TRNMANIFESTDETAIL,
     TRNMANIFESTHDR,
     REFWASTESTREAM,
     REFRTTAXCODES,
     REFINVENTORYSTATUS,
     REFINVSPLITCODES,
     REFHANDLINGCODES,
     reftreatmentgroup,
     TRNMANIFRETAILTRIP  trip
WHERE    (TRNMANIFESTDETAIL.LOC_CODE(+) = TRNINVENTORY.LOC_CODE)
     AND (TRNMANIFESTDETAIL.MAN_SYS_NUMBER(+) = TRNINVENTORY.MAN_SYS_NUMBER)
     AND (TRNMANIFESTDETAIL.MAN_PAGE_NUMBER(+) = TRNINVENTORY.MAN_PAGE_NO)
     AND (TRNMANIFESTDETAIL.MAN_LINE_NUMBER(+) = TRNINVENTORY.MAN_LINE_NO)
     AND (TRNMANIFESTHDR.LOC_CODE(+) = TRNMANIFESTDETAIL.LOC_CODE)
     AND (TRNMANIFESTHDR.MAN_SYS_NUMBER(+) = TRNMANIFESTDETAIL.MAN_SYS_NUMBER)
     AND (TRNMANIFESTHDR.MAN_PAGE_NO(+) = TRNMANIFESTDETAIL.USER_MANPAGE)
     AND (TRNMANIFESTHDR.LOC_CODE = TRNLOAD.LOC_CODE(+))
     AND (TRNMANIFESTHDR.WORK_ORDER_NUMBER = TRNLOAD.WORK_ORDER_NUMBER(+))
     AND (TRNMANIFESTHDR.LOC_CODE = REFGENERATOR.LOC_CODE(+))
     AND (TRNMANIFESTHDR.GEN_SYS_NUMBER = REFGENERATOR.GEN_SYS_NUMBER(+))
     AND (TRNMANIFESTDETAIL.LOC_CODE = REFWASTESTREAM.LOC_CODE(+))
     AND (TRNMANIFESTDETAIL.WASTE_STREAM_NUMBER = REFWASTESTREAM.WASTE_STREAM_NUMBER(+))
     AND (TRNMANIFESTDETAIL.PROFILE_NUMBER = REFWASTESTREAM.PROFILE_NUMBER(+))
     AND (REFHANDLINGCODES.LOC_CODE(+) = TRNMANIFESTDETAIL.LOC_CODE)
     AND (REFHANDLINGCODES.WASTE_STREAM_NUMBER(+) = TRNMANIFESTDETAIL.WASTE_STREAM_NUMBER)
     AND (REFHANDLINGCODES.PROFILE_NUMBER(+) = TRNMANIFESTDETAIL.PROFILE_NUMBER)
     AND (refhandlingcodes.loc_code = reftreatmentgroup.loc_code(+))
     AND (refhandlingcodes.tgroup = reftreatmentgroup.tgroup(+))
     AND (REFINVENTORYSTATUS.ACTION_ID = TRNINVENTORY.INV_STATUS)
     AND (trninventory.split_code = refinvsplitcodes.code(+))
     AND (trninventory.loc_code = refrttaxcodes.loc_code(+))
     AND (trninventory.inv_taxcode = REFRTTAXCODES.TAX_ID(+))
     AND ( (TRNINVENTORY.LOC_CODE = :ai_loc))
     and (trip.loc_code (+) = trninventory.loc_code)
     and (trip.inventory_no (+) = trninventory.inventroty_no)
     and (trip.split_no (+) = trninventory.split_no)
     AND NOT (NVL (TRNINVENTORY.IS_PRODUCT, 'N') = 'Y' OR NVL (REFWASTESTREAM.WASTE_TYPE, 'A') = 'D')
     AND nvl(TRNLOAD.WORK_ORDER_TYPE,'STD') = 'STD' 
ORDER BY TRNLOAD.WORK_ORDER_NUMBER,
     TRNINVENTORY.MAN_SYS_NUMBER,
     TRNMANIFESTDETAIL.USER_MANPAGE,
     DF_sort_string (TRNMANIFESTDETAIL.USER_MANLINE),
     TRNINVENTORY.INVENTROTY_NO,
     TRNINVENTORY.SPLIT_NO,
     TRNINVENTORY.GROUPED_DATE,
     TRNINVENTORY.GROUP_NO

解释计划

为了便于阅读,我不得不将其调整为更窄,这就是为什么有一些缩写的原因。

------------------------------------------------------------------------------------------
| Id | Operation                              | Name                | Rows | Bytes| Cost |
------------------------------------------------------------------------------------------
|  0 | SELECT STATEMENT                       |                     |  377K|  198M|  112K|
|  1 | SORT ORDER BY                          |                     |  377K|  198M|  112K|
|  2 |  HASH JOIN RIGHT OUTER                 |                     |  377K|  198M| 68645|
|  3 |   TBL ACCESS FULL                      | TRNMANIFRETAILTRIP  |  128 | 2944 |    3 |
|  4 |   HASH JOIN RIGHT OUTER                |                     |  377K|  190M|68639 |
|  5 |    TBL ACCESS FULL                     | REFTREATMENTGROUP   |  101 | 1313 |    3 |
|  6 |    FILTER                              |                     |      |      |      |
|  7 |     HASH JOIN RIGHT OUTER              |                     |  377K|  186M|68633 |
|  8 |      TBL ACCESS FULL                   | REFWASTESTREAM      |  204K|   11M|  755 |
|  9 |      HASH JOIN RIGHT OUTER             |                     |  377K|  163M|58820 |
| 10 |       TBL ACCESS FULL                  | REFGENERATOR        | 80447| 3535K|  594 |
| 11 |       FILTER                           |                     |      |      |      |
| 12 |        HASH JOIN RIGHT OUTER           |                     |  377K|  147M|50460 |
| 13 |         TBL ACCESS FULL                | TRNLOAD             |  507K|   16M|2920  |
| 14 |         HASH JOIN RIGHT OUTER          |                     |  376K|  135M|39501 |
| 15 |          TBL ACCESS FULL               | TRNMANIFESTHDR      |  844K|   49M| 4646 |
| 16 |          HASH JOIN RIGHT OUTER         |                     |  376K|  113M|26090 |
| 17 |           TBL ACCESS FULL              | REFHANDLINGCODES    |  183K| 4483K|  631 |
| 18 |           HASH JOIN RIGHT OUTER        |                     |  376K|  104M|19737 |
| 19 |            TBL ACCESS FULL             | TRNMANIFESTDETAIL   |  289K|   14M| 4496 |
| 20 |            HASH JOIN RIGHT OUTER       |                     |  376K|   85M| 9890 |
| 21 |             TBL ACCESS FULL            | REFINVSPLITCODES    |   48 |  288 |    3 |
| 22 |             HASH JOIN RIGHT OUTER      |                     |  376K|   83M| 9883 |
| 23 |              TBL ACCESS BY INDEX ROWID | REFRTTAXCODES       |    8 |   80 |    3 |
| 24 |               INDEX RANGE SCAN         | PK_TAXCODE_LOCATION |    8 |      |    1 |
| 25 |              HASH JOIN                 |                     |  376K|   80M| 9877 |
| 26 |               INDEX FULL SCAN          | IDX_INVSTATUS_TYPE  |   22 |  176 |    1 |
| 27 |               TBL ACCESS FULL          | TRNINVENTORY        |  376K|   77M| 9873 |
------------------------------------------------------------------------------------------

【问题讨论】:

每个连接都是外连接?这是为什么呢? 好问题,我怀疑很多外连接都不需要,无法想象它们都需要外连接。我在这家公司是绿色的,继承了现有的代码。像所有 IT 商店一样,有些事情会让您感到好奇。我将尝试减少外部连接,看看这是否是罪魁祸首。 ty 最近统计过吗? 统计数据是最新的,感谢您的评论。 APC 注意到一切都是外部连接的,结果证明这并不是获得业务所需的结果集所必需的。感谢所有。 【参考方案1】:

问:任何人都可以就我是否应该期待如此多的全表扫描提供高级指导,或者该 SQL 是否需要更好的优化?

答:数据是子集的地方很少,这是寻找索引调整机会的第一个地方,但我看到应用于列的函数可能意味着需要基于函数的索引。

下一个要看的地方是连接条件,连接列是否被索引。如果 Oracle 认为全表扫描同样有效或更有效,它可以选择忽略连接列上的索引。

一般来说,我会说不要担心全表扫描,但如果以下情况属实,我会担心全表扫描:

    未满足性能要求 完全扫描在大表上 运行 SQL 的会话的等待事件显示高等待有问题的大对象的“db 文件分散读取”(即全表扫描)

【讨论】:

事实证明,外部连接是罪魁祸首——在删除了可以删除的连接后,所需的内存下降到大约 60 K 而不是 198 M,成本略低于三分之一。由于这解决了问题,我没有费心查看等待事件。我没想到外连接会增加这么多内存,可能是 CPU 和磁盘,但我想我不太了解 Oracle 的内部原理! 外连接将使用大量临时表空间和 PGA 内存。如果您仍然可以满足要求并通过消除它们来加快速度,那就太好了。

以上是关于Oracle解释计划解析——Oracle做全表访问的主要内容,如果未能解决你的问题,请参考以下文章

执行计划-数据访问方式(全表扫描与4种索引的方式)

执行计划-数据访问方式(全表扫描与4种索引的方式)

Oracle执行计划总结

oracle 的hint有啥用

oracle常见的执行计划

oracle选择了错误的执行计划,怎么办