索引没有被使用

问题描述 投票:2回答:3

这是Tom Kyte的书的摘录。

“我们正在使用SELECT COUNT(*) FROM T查询(或类似的东西),我们在表T上有一个B * Tree索引。但是,优化器正在全面扫描表,而不是计算(小得多)索引条目。在这种情况下,索引可能在一组可以包含Null的列上。由于永远不会创建完全Null索引条目,因此索引中的行数不会是表中行数。这里优化器正在做正确的事情 - 如果它使用索引来计算行,它会得到错误的答案。“

据我所知,当我们使用WHERE子句时,索引会出现。为什么索引出现在上述场景中?在打击他之前我想知道事实。

sql oracle query-performance
3个回答
3
投票

“据我所知,当你使用where子句时索引会出现。”

当我们想要快速访问由索引列的特定值标识的行时,这是索引的一个用例。但还有其他用途。

计数行是一个。要计算表中Oracle实际必须计算每一行的行数(因为统计信息可能不够新鲜),这意味着从字面上读取每个存储块并计算每个块中的行。这可能是很多阅读。

但是,NOT NULL列上的索引也为表的每一行都有一个条目。索引比表(通常只有一列)小得多,因此索引块包含的条目比表块多得多。因此,Oracle必须读取少得多的索引块来获取行数,而不是扫描表所需的行数。读取更少的块比读取更多的块更快。

如果表只有可为空列的索引,则不会出现这种情况。 Oracle不会索引空值(除非索引是复合索引并且至少填充了一列),因此索引中的条目计数不能保证是表行的实际计数。

读取索引的另一个常见用例是满足SELECT语句,其中投影中的所有列都在一个索引中,索引也为任何WHERE条件提供服务。


3
投票

Oracle数据库不在B树索引中存储NULL,请参阅the documentation

除了位图索引或群集键列值为null之外,Oracle数据库不会索引所有键列为空的表行。

因此,如果已在可能包含空值的列上创建索引,则数据库无法在查询中使用此索引,例如:SELECT COUNT(*) FROM T。即使列不包含任何NULL,优化器也不会知道这一点,除非该列已标记为NOT NULL


根据the documentation - FAST FULL INDEX SCAN

快速全索引扫描

快速完整索引扫描是一种完整索引扫描,其中数据库访问索引本身的数据而不访问该表,并且数据库不按特定顺序读取索引块。

当满足以下两个条件时,快速全索引扫描是全表扫描的替代方法:

索引必须包含查询所需的所有列。

包含所有空值的行不得出现在查询结果集中。为了保证这个结果,索引中至少有一列必须具有:

  • NOT NULL约束
  • 应用于列的谓词,用于防止在查询结果集中考虑空值

因此,如果您知道索引列不能包含NULL值,则使用NOT NULL将此列标记为ALTER TABLE table_name MODIFY column_name column_type NOT NULL;,数据库将在查询中使用该索引:SELECT COUNT(*) FROM T 如果colum可以有空值,并且不能标记为NOT NULL,那么请使用@Gordon Linoff的答案解决方案。


2
投票

您可以通过在索引中包含常量来强制索引NULL值:

create index t_table_col on t(col, 0);

1是一个永远的表达,从来没有NULL

© www.soinside.com 2019 - 2024. All rights reserved.