Oracle Explain Plan分析——Oracle Doing Full Table Access

Analysis of Oracle Explain Plan - Oracle Doing Full Table Access

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

每个 table 都有索引并使用了适当的列,因此不清楚为什么 Oracle 不使用这些索引。任何人都可以就我是否应该期待如此多的完整 table 扫描,或者 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 |
------------------------------------------------------------------------------------------

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

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

下一个要查看的地方是连接条件,连接列是否已编入索引。如果 Oracle 认为完全 table 扫描同样高效或更高效,则它可以选择忽略连接列上的索引。

一般来说,我会说不要担心完全 table 扫描,除非,如果满足以下条件,我会担心完全 table 扫描:

  1. 未满足性能要求
  2. 完整扫描是大 tables
  3. 会话的等待事件 运行 SQL 显示对 "db file scattered read" 有问题的大对象的高等待(即完全 table 扫描)