这是汤姆·凯特(Tom Kyte)的书的摘录。
“我们正在使用
SELECT COUNT(*) FROM T
查询(或类似的查询) 并且在表T上有一个B * Tree索引。但是,优化器已满 扫描表,而不是计算(小得多的)索引 条目。在这种情况下,索引可能位于一组 可以包含Null。由于完全为空的索引条目永远不会 进行时,索引中的行数将不会是 桌子。优化器在这里做正确的事,它将得到 如果它使用索引对行进行计数,则答案错误。“
据我所知,当我们使用WHERE子句时,索引就会出现。为什么索引会出现在上述情况下?在对抗他之前,我想知道事实。
答案 0 :(得分:3)
Oracle数据库不在B树索引中存储NULL,请参见the documentation
Oracle数据库不索引所有键列均在其中的表行 null,除了位图索引或群集键列值时 为空。
因此,如果在可能包含空值的列上创建了索引,则数据库无法在诸如SELECT COUNT(*) FROM T
之类的查询中使用该索引。即使该列不包含任何NULL,优化器也不知道这一点,除非该列已被标记为NOT NULL
。
根据the documentation - FAST FULL INDEX SCAN
快速全索引扫描
快速全索引扫描是指 数据库在没有索引的情况下访问索引本身中的数据 访问表,并且数据库以no的方式读取索引块 特定顺序。
在以下情况下,快速全索引扫描可替代全表扫描 同时满足以下两个条件:
索引必须包含查询所需的所有列。
包含所有空值的行不得出现在查询结果集中。 为保证此结果,索引中至少有一列 必须具有以下任一条件:
一个非空约束
应用于该列的谓词,可防止空值被使用 在查询结果集中考虑
因此,如果您知道索引列不能包含NULL值,则使用NOT NULL
将此列标记为ALTER TABLE table_name MODIFY column_name column_type NOT NULL;
,数据库将在查询中使用该索引:SELECT COUNT(*) FROM T
如果该列可以为空,并且不能标记为NOT NULL
,请使用@Gordon Linoff答案中的解决方案。
答案 1 :(得分:3)
“据我所知,当您使用where子句时,索引会显示在图片中。”
当我们想快速访问由索引列的特定值标识的行时,这就是索引的一种使用情况。但是还有其他用途。
计数行为一。为了计算表中的行数,Oracle实际上必须计算每行(因为统计信息可能不够新鲜),这意味着从字面上读取每个存储块并计算每个块中的行。可能有很多读物。
但是,NOT NULL列上的索引也为表的每一行都有一个条目。索引比表小得多(通常只有一列),因此索引块比表块包含更多的条目。因此,与行扫描表相比,Oracle必须读取少得多的Index块才能获得行数。读取更少的块比读取更多的块要快。
如果表仅在可为空的列上具有索引,则情况并非如此。 Oracle不会索引空值(除非索引是一个复合索引并且至少填充了一个列),因此索引中条目的计数不能保证是表行的实际计数。
读取索引的另一种常见用例是满足SELECT语句,其中投影中的所有列都在一个索引中,并且该索引还满足任何WHERE条件。
答案 2 :(得分:2)
您可以通过在索引中包含常量来强制NULL
值的索引:
create index t_table_col on t(col, 0);
1
是一个永远不会NULL
的常量表达式。