Sql 为什么索引不用于此查询?

Sql 为什么索引不用于此查询?,sql,performance,oracle,indexing,Sql,Performance,Oracle,Indexing,我有一个查询,在我认为可能的时候没有使用索引,因此出于好奇,我复制了它: 创建一个test_表,其中包含1.000.000行10个不同的col值,一些_数据中包含500字节的数据 尝试获取col和计数的不同值: 不使用索引,但前提是提示不会改变这一点 我想,在这种情况下不能使用索引,但为什么?更新: 尝试使col列不为NULL。这就是它不使用索引的原因。当它不为空时,计划如下 SELECT STATEMENT, GOAL = ALL_ROWS 69 10 30

我有一个查询,在我认为可能的时候没有使用索引,因此出于好奇,我复制了它:

创建一个test_表,其中包含1.000.000行10个不同的col值,一些_数据中包含500字节的数据

尝试获取col和计数的不同值:

不使用索引,但前提是提示不会改变这一点

我想,在这种情况下不能使用索引,但为什么?

更新: 尝试使col列不为NULL。这就是它不使用索引的原因。当它不为空时,计划如下

SELECT STATEMENT, GOAL = ALL_ROWS           69  10  30
                    HASH GROUP BY           69  10  30
 INDEX FAST FULL SCAN   SANDBOX TEST_INDEX  56  98072   294216
如果优化器确定不使用索引更有效,可能是因为重写了查询,那么它不会。优化器提示就是这样的,即告诉Oracle您希望它使用的索引的提示。你可以把它们看作是建议。但是,如果优化器认为最好不要再次使用索引(例如,由于查询重写),那么它就不会这样做

请参阅此链接: 指定其中一个提示会导致优化器仅在访问路径基于索引或集群的存在以及SQL语句的语法结构可用时才选择指定的访问路径。如果提示指定了不可用的访问路径,那么优化器将忽略它

由于您正在运行count*操作,优化器已确定只扫描整个表和哈希而不使用索引更有效

下面是关于提示的另一个便捷链接:

我运行了彼得的原始资料并复制了他的结果。然后我应用dcp的建议

SQL> alter table test_table modify col not null;

Table altered.

SQL> EXEC dbms_stats.gather_table_stats( user, 'TEST_TABLE' , cascade=>true)

PL/SQL procedure successfully completed.

SQL> EXPLAIN PLAN FOR
  2    SELECT col, COUNT(*)
  3    FROM test_table
  4    GROUP BY col;

Explained.

SQL> select * from table(dbms_xplan.display)
  2  /

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------
Plan hash value: 2099921975

------------------------------------------------------------------------------------
| Id  | Operation             | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |            |    10 |    30 |   574   (9)| 00:00:07 |
|   1 |  HASH GROUP BY        |            |    10 |    30 |   574   (9)| 00:00:07 |
|   2 |   INDEX FAST FULL SCAN| TEST_INDEX |  1000K|  2929K|   532   (2)| 00:00:07 |
------------------------------------------------------------------------------------

9 rows selected.

SQL>
这之所以重要,是因为正常的B树索引中不包含NULL值,但GROUP BY必须在查询中包含NULL作为分组值。通过告诉优化器col中没有空值,可以自由地使用效率更高的索引,我得到的FTS运行时间几乎为3.55秒。这是元数据如何影响优化器的经典示例


顺便说一句,这显然是一个10g或11g数据库,因为它使用哈希分组算法,而不是旧的排序分组算法

您忘记了这个非常重要的信息:COL不是null

如果列可为空,则无法使用索引,因为可能存在未编制索引的行

SQL> ALTER TABLE test_table MODIFY (col NOT NULL);

Table altered
SQL> EXPLAIN PLAN FOR
  2  SELECT col, COUNT(*) FROM test_table GROUP BY col;

Explained
SQL> SELECT * FROM table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1077170955
--------------------------------------------------------------------------------
| Id  | Operation            | Name       | Rows  | Bytes | Cost (%CPU)| Time
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |            |    10 |    30 |  1954   (1)| 00:00:2
|   1 |  SORT GROUP BY NOSORT|            |    10 |    30 |  1954   (1)| 00:00:2
|   2 |   INDEX FULL SCAN    | TEST_INDEX |   976K|  2861K|  1954   (1)| 00:00:2
--------------------------------------------------------------------------------

位图索引也可以

Execution Plan ---------------------------------------------------------- Plan hash value: 2200191467 --------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 10 | 30 | 15983 (2)| 00:03:12 | | 1 | HASH GROUP BY | | 10 | 30 | 15983 (2)| 00:03:12 | | 2 | TABLE ACCESS FULL| TEST_TABLE | 1013K| 2968K| 15825 (1)| 00:03:10 | --------------------------------------------------------------------------------- SQL> create bitmap index test_index on test_table(col); Index created. SQL> EXEC dbms_stats.gather_table_stats( 'MY_SCHEMA', 'TEST_TABLE' ); PL/SQL procedure successfully completed. SQL> SELECT col, COUNT(*) 2 FROM test_table 3 GROUP BY col 4 / Execution Plan ---------------------------------------------------------- Plan hash value: 238193838 --------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 10 | 30 | 286 (0)| 00:00:04 | | 1 | SORT GROUP BY NOSORT | | 10 | 30 | 286 (0)| 00:00:04 | | 2 | BITMAP CONVERSION COUNT| | 1010K| 2961K| 286 (0)| 00:00:04 | | 3 | BITMAP INDEX FULL SCAN| TEST_INDEX | | | | | ---------------------------------------------------------------------------------------
使用索引无法阻止完全扫描,因此它实际上没有任何好处。@recursive:看起来是这样,但是为什么索引的完全扫描不比完全表扫描更可取?如果您在这里找不到解决方案,你可以试试asktom.oracle.com,它在过去对我很有帮助。查询中没有输出表中的任何数据,只有col和count*。整个结果可以通过扫描索引生成。如果扫描索引而不是表会更快,即使它必须以任何一种方式进行完全扫描,这似乎不符合逻辑吗?索引更小。@dcp:我知道提示是如何工作的,我很好奇为什么索引没有首先被使用。@Sam/@Peter Lang-你可以看到我的最新更新以获得答案。我认为Oracle无法从索引中确定空值的计数,因此无法使用索引扫描。+1:非常好,谢谢你为我们指明了正确的方向!不过我还是要接受APCs的回答,因为它提供了有关这种行为原因的更多细节。@dcp-对不起,我需要吃更多的胡萝卜。我已经把灯笼裤修好了,没问题。嘿,至少你没打毒品:。谢谢!接受此答案,因为它提供了有关此行为原因的详细信息。位图索引仅适用于有限范围的表。如果我们的表受到大量插入、更新或删除的影响,那么维护索引的成本可能太高。
SQL> alter table test_table modify col not null;

Table altered.

SQL> EXEC dbms_stats.gather_table_stats( user, 'TEST_TABLE' , cascade=>true)

PL/SQL procedure successfully completed.

SQL> EXPLAIN PLAN FOR
  2    SELECT col, COUNT(*)
  3    FROM test_table
  4    GROUP BY col;

Explained.

SQL> select * from table(dbms_xplan.display)
  2  /

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------
Plan hash value: 2099921975

------------------------------------------------------------------------------------
| Id  | Operation             | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |            |    10 |    30 |   574   (9)| 00:00:07 |
|   1 |  HASH GROUP BY        |            |    10 |    30 |   574   (9)| 00:00:07 |
|   2 |   INDEX FAST FULL SCAN| TEST_INDEX |  1000K|  2929K|   532   (2)| 00:00:07 |
------------------------------------------------------------------------------------

9 rows selected.

SQL>
SQL> ALTER TABLE test_table MODIFY (col NOT NULL);

Table altered
SQL> EXPLAIN PLAN FOR
  2  SELECT col, COUNT(*) FROM test_table GROUP BY col;

Explained
SQL> SELECT * FROM table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1077170955
--------------------------------------------------------------------------------
| Id  | Operation            | Name       | Rows  | Bytes | Cost (%CPU)| Time
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |            |    10 |    30 |  1954   (1)| 00:00:2
|   1 |  SORT GROUP BY NOSORT|            |    10 |    30 |  1954   (1)| 00:00:2
|   2 |   INDEX FULL SCAN    | TEST_INDEX |   976K|  2861K|  1954   (1)| 00:00:2
--------------------------------------------------------------------------------
Execution Plan ---------------------------------------------------------- Plan hash value: 2200191467 --------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 10 | 30 | 15983 (2)| 00:03:12 | | 1 | HASH GROUP BY | | 10 | 30 | 15983 (2)| 00:03:12 | | 2 | TABLE ACCESS FULL| TEST_TABLE | 1013K| 2968K| 15825 (1)| 00:03:10 | --------------------------------------------------------------------------------- SQL> create bitmap index test_index on test_table(col); Index created. SQL> EXEC dbms_stats.gather_table_stats( 'MY_SCHEMA', 'TEST_TABLE' ); PL/SQL procedure successfully completed. SQL> SELECT col, COUNT(*) 2 FROM test_table 3 GROUP BY col 4 / Execution Plan ---------------------------------------------------------- Plan hash value: 238193838 --------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 10 | 30 | 286 (0)| 00:00:04 | | 1 | SORT GROUP BY NOSORT | | 10 | 30 | 286 (0)| 00:00:04 | | 2 | BITMAP CONVERSION COUNT| | 1010K| 2961K| 286 (0)| 00:00:04 | | 3 | BITMAP INDEX FULL SCAN| TEST_INDEX | | | | | ---------------------------------------------------------------------------------------