索引未被使用

Index is not getting used

这是 Tom Kyte 的书的节选。

"We’re using a SELECT COUNT(*) FROM T query (or something similar) and we have a B*Tree index on table T. However, the optimizer is full scanning the table, rather than counting the (much smaller) index entries. In this case, the index is probably on a set of columns that can contain Nulls. Since a totally Null index entry would never be made, the count of rows in the index will not be the count of rows in the table. Here the optimizer is doing the right thing—it would get the wrong answer if it used the index to count rows."

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

您可以通过在索引中包含常量来强制索引 NULL 值:

create index t_table_col on t(col, 0);

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

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

Oracle Database does not index table rows in which all key columns are null, except for bitmap indexes or when the cluster key column value is null.

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


根据the documentation - FAST FULL INDEX SCAN

Fast Full Index Scan

A fast full index scan is a full index scan in which the database accesses the data in the index itself without accessing the table, and the database reads the index blocks in no particular order.

Fast full index scans are an alternative to a full table scan when both of the following conditions are met:

The index must contain all columns needed for the query.

A row containing all nulls must not appear in the query result set. For this result to be guaranteed, at least one column in the index must have either:

  • A NOT NULL constraint

  • A predicate applied to the column that prevents nulls from being considered in the query result set

因此,如果您知道索引列不能包含 NULL 值,则使用 ALTER TABLE table_name MODIFY column_name column_type NOT NULL; 将此列标记为 NOT NULL,数据库将在查询中使用该索引:SELECT COUNT(*) FROM T

如果列可以有空值,并且不能标记为 NOT NULL,则使用@Gordon Linoff 的答案中的解决方案。

"As far as i know indexes comes in picture when you used where clause. "

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

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

但是,NOT NULL 列上的索引对于 table 的每一行也有一个条目。索引比 table 小得多(通常只有一列),因此索引块包含的条目比 Table 块多得多。因此,与扫描 table 所需的相比,Oracle 必须读取更少的索引块来获取行数。读取更少的块比读取更多的块更快。

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

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