索引没有被使用
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 可以,如果有合适的索引并且优化器选择使用它。 请注意,您可以通过使用零而不是一来为每行节省一个字节的存储空间。以上是关于索引没有被使用的主要内容,如果未能解决你的问题,请参考以下文章