这是Tom Kyte的书的摘录。
“我们正在使用
SELECT COUNT(*) FROM T
查询(或类似的东西),我们在表T上有一个B * Tree索引。但是,优化器正在全面扫描表,而不是计算(小得多)索引条目。在这种情况下,索引可能在一组可以包含Null的列上。由于永远不会创建完全Null索引条目,因此索引中的行数不会是表中行数。这里优化器正在做正确的事情 - 如果它使用索引来计算行,它会得到错误的答案。“
据我所知,当我们使用WHERE子句时,索引会出现。为什么索引出现在上述场景中?在打击他之前我想知道事实。
“据我所知,当你使用where子句时索引会出现。”
当我们想要快速访问由索引列的特定值标识的行时,这是索引的一个用例。但还有其他用途。
计数行是一个。要计算表中Oracle实际必须计算每一行的行数(因为统计信息可能不够新鲜),这意味着从字面上读取每个存储块并计算每个块中的行。这可能是很多阅读。
但是,NOT NULL列上的索引也为表的每一行都有一个条目。索引比表(通常只有一列)小得多,因此索引块包含的条目比表块多得多。因此,Oracle必须读取少得多的索引块来获取行数,而不是扫描表所需的行数。读取更少的块比读取更多的块更快。
如果表只有可为空列的索引,则不会出现这种情况。 Oracle不会索引空值(除非索引是复合索引并且至少填充了一列),因此索引中的条目计数不能保证是表行的实际计数。
读取索引的另一个常见用例是满足SELECT语句,其中投影中的所有列都在一个索引中,索引也为任何WHERE条件提供服务。
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的答案解决方案。
您可以通过在索引中包含常量来强制索引NULL
值:
create index t_table_col on t(col, 0);
1
是一个永远的表达,从来没有NULL
。