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做全表访问的主要内容,如果未能解决你的问题,请参考以下文章