【问题标题】:Oracle SQL - Select not using index as expectedOracle SQL - 选择不按预期使用索引
【发布时间】:2020-06-22 13:12:59
【问题描述】:

所以我已经超过 5 年没有使用 Oracle 并且我已经没有实践了。我一直在使用 SQL Server。

我正在查看一些现有的查询并尝试改进它们,但它们的反应非常奇怪。根据解释计划,他们并没有加快速度,而是进行全表扫描而不使用索引。

在原始查询中,在 where 语句中完成了两个表之间的等值连接。我们将它们称为表 A 和 B。我使用了一个解释计划,然后是 SELECT * FROM table(DBMS_XPLAN.DISPLAY (FORMAT=>'ALL +OUTLINE'));它告诉我表 A 是由本地索引查询的。
按本地索引 ROWID 访问表

SELECT A.* 
FROM TableA  A, TableB B 
WHERE A.SecondaryID = B.ID;

我尝试更改查询并将 TableA 与新表(表 C)连接起来。表 C 是表 B 的子集,有 700 条记录,而不是 100K。然而,解释计划告诉我,现在通过完整查找查询表 A。

CREATE TableC 
AS<br>
SELECT * FROM TableB WHERE Active='Y';



SELECT A.* 
FROM TableA  A, TableC C 
WHERE A.SecondaryID = C.ID;

下一步,我保留了表 A 和 C 之间的连接,但使用了一个提示来告诉它使用表 A 上的索引。但是它仍然会进行完整的查找。

SELECT /*+ INDEX (A_NDX01) */ A.* 
FROM TableA  A, TableC C 
WHERE A.SecondaryID = C.ID;

所以我尝试将连接更改为表 A 的简单选择,并使用 IN 语句与表 C 进行比较。仍然是全表扫描。

SELECT A.* 
FROM TableA  A 
WHERE A.SecondaryID in (SELECT ID FROM TableC);

最后,我把前面的语句改成 subselect 来拉取前 1000 条记录,它使用了索引。奇怪的是,表 C 中只有 700 条记录。

SELECT A.* 
FROM TableA  A 
WHERE A.SecondaryID in (SELECT ID FROM TableC WHERE rownum <1000

)

我想知道是否有人可以帮我弄清楚发生了什么?
我最好的猜测是,由于 TableC 是一个新表,也许优化器不知道其中有多少条记录,这就是为什么它只在知道少于 1000 条记录时才使用索引的原因?
我尝试在我的模式上运行 dbms_stats.gather_schema_stats 但它没有帮助。
感谢您的帮助。

【问题讨论】:

  • 确保所有相关表的统计信息都是最新的 (SELECT TABLE_NAME, LAST_ANALYZED FROM DBA_TABLES WHERE TABLE_NAME IN ('TABLE1', 'TABLE2', 'TABLE3')),如果需要,请使用 DBMS_STATS.GATHER_TABLE STATS 收集统计信息。然后再次检查您的查询计划。
  • 所以 last_analyzed 已经很老了。但是 DBA_TAB_STATS_HISTORY 中每个分区的统计信息是最近的。我不确定为什么会有差异。

标签: sql oracle indexing


【解决方案1】:

作为一般规则,使用索引并不一定会让您的查询总是更快。

提示是优化器使用路径的指令,它并不意味着优化器会选择服从提示指令。在这种情况下,优化器会认为 TableA 上的索引查找在

SELECT A.* 
FROM TableA  A, TableB B 
WHERE A.SecondaryID = B.ID;

SELECT /*+ INDEX (A_NDX01) */ A.* 
FROM TableA  A, TableC C 
WHERE A.SecondaryID = C.ID;

SELECT A.* 
FROM TableA  A 
WHERE A.SecondaryID in (SELECT ID FROM TableC);

在内部,它可能已将所有这些语句 (IN) 转换为一个连接,在考虑 tableA 和 tableC 中的数据时决定使用全表扫描。

当您执行 rownum 条件时,此计划转换并未完成。这是因为当查询块中有 rownum 时,视图合并将不起作用。

我相信这就是你这样做时正在发生的事情

SELECT A.* 
FROM TableA  A 
WHERE A.SecondaryID in (SELECT ID FROM TableC WHERE rownum <1000)

看看下面的链接

Oracle. Preventing merge subquery and main query conditions

【讨论】:

  • 你说:“提示是优化器使用路径的指令,它并不意味着优化器会选择服从提示指令。”根据优化器的 Oracle 产品经理的说法,如果可以,它遵守该指令。在某些情况下它不能,在这种情况下它当然不会。我不知道为什么这种情况下的提示有问题。
  • 您好 Stew,为了更好地理解这一点,当我明确提到在查询中使用索引提示时,优化器是否会服从,即使它对全表扫描更有效。我一直认为这是基于规则的优化器,它会始终遵守基于规则的规则
  • 是的。这个词是“指令”。如果可以,优化器将遵循提示。但是,它可能会对查询进行不同的转换,这意味着提示变得无效或不相关。例如,即使您从一个表中请求数据而另一个表不相关,您也可以编写一个带有连接的查询。 Oracle 将优化掉另一个表,因此任何关于如何访问它的提示都变得毫无意义。
猜你喜欢
  • 1970-01-01
  • 2019-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-17
  • 2016-11-21
  • 1970-01-01
  • 2020-01-22
相关资源
最近更新 更多