如何强制oracle使用索引范围扫描?

wsb*_*383 13 oracle indexing

我有一系列非常相似的查询,我针对一个包含14亿条记录(带索引)的表运行,唯一的问题是这些查询中至少有10%的执行时间比其他查询多出100倍.

我运行了一个解释计划并注意到快速查询(大约90%)Oracle正在使用索引范围扫描; 在慢速的,它使用完整的索引扫描.

有没有办法强制Oracle进行索引范围扫描?

WW.*_*WW. 12

我建议采用以下方法: -

  • 在慢速声明中获得解释计划
  • 使用INDEX提示,获取有关使用索引的解释计划

您会注意到INDEX计划的成本更高.这就是Oracle不选择索引计划的原因.成本是甲骨文基于其统计数据和各种假设的估计.

如果计划的估计成本更高,但实际上运行得更快,那么估计是错误的.你的工作是找出估计错误的原因并纠正错误.然后,Oracle将为此声明选择正确的计划,并为其自己选择其他声明.

要弄清楚它为什么是错误的,请查看计划中预期行的数量.你可能会发现其中一个是一个数量级的.这可能是由于非均匀分布的列值,旧统计信息,彼此核心的列等.

要解决此问题,您可以让Oracle收集更好的统计信息并使用更好的启动假设进行提示.然后它将估算准确的成本并提出最快的计划.

如果您发布更多信息,我可以进一步发表评论.

  • 鉴于它是一个范围扫描,我想知道"慢"查询是否处理更大的范围(例如一年而不是一周).在这种情况下,你会期望它们相当慢.在某些时候,随着范围变得足够大,从索引切换到完全扫描是有意义的.Oracle是否正确地猜到了这一点是另一回事. (3认同)

wol*_*lφi 9

要"强制"Oracle使用索引范围扫描,只需使用优化程序提示INDEX_RS_ASC.例如:

CREATE TABLE mytable (a NUMBER NOT NULL, b NUMBER NOT NULL, c CHAR(10)) NOLOGGING;

INSERT /*+ APPEND */ INTO mytable(a,b,c) 
SELECT level, mod(level,100)+1, 'a'  FROM dual CONNECT BY level <= 1E6;

CREATE INDEX myindex_ba ON mytable(b, a);
EXECUTE dbms_stats.gather_table_stats(NULL,'mytable');

SELECT /*+ FULL(m)         */ b FROM mytable m WHERE b=10; -- full table scan
SELECT /*+ INDEX_RS_ASC(m) */ b FROM mytable m WHERE b=10; -- index range scan
SELECT /*+ INDEX_FFS(m)    */ b FROM mytable m WHERE b=10; -- index fast full scan
Run Code Online (Sandbox Code Playgroud)

这是否会使您的查询实际运行得更快取决于许多因素,例如索引值的选择性或表中行的物理顺序.例如,如果将查询更改为WHERE b BETWEEN 10 AND <xxx>,则以下成本将显示在计算机的执行计划中:

b BETWEEN 10 AND    10     20      40     80
FULL               749    750     751    752
INDEX_RS_ASC        29    325     865   1943
INDEX_FFS          597    598     599    601
Run Code Online (Sandbox Code Playgroud)

如果您稍微更改查询,不仅要选择索引列b,还要选择其他非索引列,则成本会发生显着变化:

b BETWEEN 10 AND    10     20      40     80
FULL               749    750     751    754
INDEX_RS_ASC      3352  40540  108215 243563
INDEX_FFS         3352  40540  108215 243563
Run Code Online (Sandbox Code Playgroud)