索引没有被使用

时间:2018-09-02 08:55:03

标签: sql oracle query-performance

这是汤姆·凯特(Tom Kyte)的书的摘录。

  

“我们正在使用SELECT COUNT(*) FROM T查询(或类似的查询)   并且在表T上有一个B * Tree索引。但是,优化器已满   扫描表,而不是计算(小得多的)索引   条目。在这种情况下,索引可能位于一组   可以包含Null。由于完全为空的索引条目永远不会   进行时,索引中的行数将不会是   桌子。优化器在这里做正确的事,它将得到   如果它使用索引对行进行计数,则答案错误。“

据我所知,当我们使用WHERE子句时,索引就会出现。为什么索引会出现在上述情况下?在对抗他之前,我想知道事实。

3 个答案:

答案 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的常量表达式。