Oracle 11G - 未在 Join 上使用 PK 索引,采用全扫描
Posted
技术标签:
【中文标题】Oracle 11G - 未在 Join 上使用 PK 索引,采用全扫描【英文标题】:Oracle 11G - PK index not being used on Join, resorting to full scan 【发布时间】:2012-07-19 18:16:01 【问题描述】:我知道 Oracle 有时会“判断”执行全表扫描而不是索引扫描更好,但仍处于“学习阶段”,我只是想更好地了解 Oracle 何时会执行确定最佳路线。例如,我有一个简单的查询:
Select *
FROM GLMV_JOURNAL_LOGS JLOG
INNER JOIN GLMV_Transact_Details TDTL
ON TDTL.TR_REF_NO = JLOG.TR_REF_NO
AND TDTL.SCAT_KEY = JLOG.Scat_key
AND TDTL.CASE_KEY = JLOG.CASE_KEY
AND TDTL.TR_CD = JLOG.TR_CD
INNER JOIN FUND_DESC FDDC
ON FDDC.FD_DESC_ID = TDTL.FD_DESC_ID
INNER JOIN FD_RATES FDRT
ON FDRT.FDRT_KEY = TDTL.FDRT_KEY
INNER JOIN BEN_TYPES BNTP
ON BNTP.BNTP_KEY = FDRT.BNTP_KEY
WHERE JLOG.JRNL_CD = '0'
AND JLOG.SRC_CD = '2'
AND JLOG.MKEY_FD_NUM <> 0
AND NVL(JLOG.TMOV_KEY, -1) > 0
AND NVL(JLOG.ORIG_SCAT_KEY, 1) = 1
AND TDTL.STAT_CD <> '4'
AND NVL(TDTL.ORIG_SCAT_KEY, 1) = 1
FD_RATES 上的连接是 PK 值上的连接,我还在 GLMV_Transact_Details 上创建了一个相应的索引,认为会阻止全表扫描,但是,根据下面的解释计划,它不是,即使在我已经执行索引重建并收集表统计信息,结果仍然相同:
现在,如果我进入查询并添加以下 where 子句:
AND FDRT.FDRT_KEY = 100
索引当然会启动,但我想我很好奇为什么它不是在进行内部连接时......有什么提示吗??
【问题讨论】:
【参考方案1】:优化器估计 FD_RATES 上的全表扫描成本为 106。此馈入的连接的估计基数为 416,馈送该连接的其他行源也是如此。如果我们用嵌套循环替换散列连接,对每一行进行唯一索引 (PK) 查找,那么对于循环的每次迭代,成本将至少为 1,可能为 2 或 3,我们认为会有是 416 次迭代,所以这将是至少 416 的成本,可能是两倍或三倍,这比进行全表扫描的估计成本要高得多。
现在,估计可能是错误的。根据我的经验,主要要看的是计划中显示的基数。如果这些都相当准确,那么 Oracle 很有可能选择了相当有效的连接顺序和访问路径——不一定是最有效的,但很接近。
如果您想尝试强制执行索引扫描以查看其执行情况,我相信您想要的提示是:
Select /*+ INDEX(fdrt) */ *
...
【讨论】:
【参考方案2】:我将忽略您的示例,并尝试回答您的 FTS 与索引问题:)
通常,使用索引的原因是为了最大限度地减少您需要读取的数据块以满足您的查询。这很大程度上取决于您的数据在表中的物理存储方式。您选择的行的百分比没有区别,而是通过使用索引而不是全表扫描可以避免多少块。例如,如果您有一个包含 3000 个块的 3000 万行的表,并且您想选择 15,000 行(或 0.5%),您应该使用索引吗?好吧,如果所有 15,000 行都在最后 200 个块中,那么索引绝对是有意义的。但是,如果查询必须从每个数据块中获取 5 行才能获得 15,000 行,则全表扫描更有意义,因为无论如何您都必须接触所有数据块。
Cary Millsap 举了一个很好的例子,将 Oracle 索引视为书中的索引。如果您有一本关于 oracle 的书,并且您查找“分区”,它可能会指出您在相对较小的一组页面中出现了很多事件。所以在这种情况下,使用索引是个好主意。但是,如果您查找“Row”,它的出现次数可能与“Partition”相同,但它们将分布在大多数页面中。在这种情况下,最好“全扫描”并按顺序阅读每一页,而不是在页面和索引之间来回翻转。
Oracle 在您的索引中存储有关表中数据的物理存储方式的近似值,并在确定选择哪个路径时使用该信息。显然,优化器的作用远不止于此(您可能会因错误的统计数据或参数设置而毁掉它),但这应该可以帮助您入门。
【讨论】:
【参考方案3】:我没有足够的信息来提供完整的答案,但这里有一些说明:
我不会说像您这样简单的 5 表连接。可以不放一张桌子吗? 只有当表很大时,全表扫描才不好。对于较小的表,这与基于索引的访问没有区别。 您可以使用提示(嵌入到 cmets 中)来指导优化器使用特定的访问路径。【讨论】:
感谢您的回复...即使只有 2 个表(transact_details 和 FD_Rates),仍会进行全表扫描。 FD_Rates 不是很大,但是 transact_details 有超过 2600 万行。通常在 JOINS 上使用哪些类型的提示来强制使用索引?如果我至少能够理解“何时”或“如何”强制它,至少它会给我一些测试和基准,看看它在结果中是否值得。以上是关于Oracle 11G - 未在 Join 上使用 PK 索引,采用全扫描的主要内容,如果未能解决你的问题,请参考以下文章
Oracle11g链接提示未“在本地计算机注册“OraOLEDB.Oracle”解决方法
是否有关于使用显式 ANSI JOIN 与隐式连接的 Oracle 官方建议?