索引没有被使用

Posted

技术标签:

【中文标题】索引没有被使用【英文标题】:Index is not getting used 【发布时间】:2018-09-02 08:55:02 【问题描述】:

这是汤姆·凯特的书的节选。

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

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

【问题讨论】:

“使用 where 子句时出现索引” - 这不是索引使用的唯一场景。如果优化器这么认为,一个简单的 select 语句就可以很好地利用索引。 【参考方案1】:

“据我所知,当您使用 where 子句时,索引会出现在图片中。”

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

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

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

如果表仅在可空列上有索引,则不是这样。 Oracle 不索引空值(除非索引是复合索引并且至少填充了一列),因此索引中的条目数不能保证是表行的实际计数。

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

【讨论】:

【参考方案2】:

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

Oracle 数据库不会索引所有键列都在其中的表行 null,位图索引或簇键列值时除外 为空。

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


根据the documentation - FAST FULL INDEX SCAN

快速全索引扫描

快速全索引扫描是一种全索引扫描 数据库访问索引本身中的数据而不 访问表,数据库读取索引块 特定的顺序。

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

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

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

非空约束

应用于列的谓词可防止出现空值 在查询结果集中考虑

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

【讨论】:

【参考方案3】:

您可以通过在索引中包含一个常量来强制对 NULL 值进行索引:

create index t_table_col on t(col, 0);

1 是一个永远不会是NULL 的常量表达式。

【讨论】:

我的问题是图片中的索引是如何出现的。我们没有使用 where 子句。每当我们使用 count(*) 时,oracle 会计算索引吗? @OnkarTiwari 可以,如果有合适的索引并且优化器选择使用它。 请注意,您可以通过使用零而不是一来为每行节省一个字节的存储空间。

以上是关于索引没有被使用的主要内容,如果未能解决你的问题,请参考以下文章

TiDB 索引没有被用于查询

有没有办法使用查询工具通过 solr 管理面板查看仅被索引但未存储的搜索文档字段?

sql语句中导致索引失败的一些错误使用方式

为啥在由数组实现的堆中,索引 0 未被使用?

Dijkstra 算法中的节点没有被保存

索引无效原因