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

Pet*_*ang 12 sql oracle indexing performance

我有一个查询,当我认为它没有使用索引时,所以我出于好奇而转载它:

创建一个test_table包含1.000.000行(10个不同的值col,500个字节的数据some_data).

CREATE TABLE test_table AS (
  SELECT MOD(ROWNUM,10) col, LPAD('x', 500, 'x') some_data
  FROM dual
  CONNECT BY ROWNUM <= 1000000
);
Run Code Online (Sandbox Code Playgroud)

创建索引并收集表统计信息:

CREATE INDEX test_index ON test_table ( col );

EXEC dbms_stats.gather_table_stats( 'MY_SCHEMA', 'TEST_TABLE' );
Run Code Online (Sandbox Code Playgroud)

尝试获得不同的值colCOUNT:

EXPLAIN PLAN FOR
  SELECT col, COUNT(*)
  FROM test_table
  GROUP BY col;

---------------------------------------------------------------------------------
| Id  | Operation          | Name       | Rows  | Bytes | Cost (%CPU)| Time
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |            |    10 |    30 | 15816   (1)| 00:03:10 
|   1 |  HASH GROUP BY     |            |    10 |    30 | 15816   (1)| 00:03:10 
|   2 |   TABLE ACCESS FULL| TEST_TABLE |   994K|  2914K| 15755   (1)| 00:03:10 
--------------------------------------------------------------------------------- 
Run Code Online (Sandbox Code Playgroud)

如果提示不改变,则不使用索引.

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

dcp*_*dcp 13

更新: 尝试使col列NOT 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
Run Code Online (Sandbox Code Playgroud)

如果优化器确定不使用索引更有效(可能是由于重写查询),那么它就不会.优化提示是正义的,即提示来告诉Oracle:你的索引喜欢使用它.您可以将它们视为建议.但是,如果优化器确定最好不使用索引(例如,再次,作为查询重写的结果),那么它不会.

请参阅此链接:http://download.oracle.com/docs/cd/B19306_01/server.102/b14211/hintsref.htm "指定其中一个提示会导致优化器仅在访问路径时选择指定的访问路径基于索引或集群的存在以及SQL语句的语法结构是可用的.如果提示指定了不可用的访问路径,则优化器会忽略它.

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

这是关于提示的另一个方便的链接:http: //www.dba-oracle.com/t_hint_ignored.htm

  • 我不认为Oracle可以从索引中确定NULL的计数,因此无法使用索引扫描. (3认同)

Vin*_*rat 10

你忘了这个非常重要的信息:COL不是空的

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

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
--------------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)


APC*_*APC 5

我跑了彼得原创的东西并复制了他的结果.然后我应用了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>
Run Code Online (Sandbox Code Playgroud)

这很重要的原因是因为NULL值不包含在普通的B-TREE索引中,但GROUP BY必须在查询中包含NULL作为分组"值".通过告诉优化器没有NULL,col可以自由使用效率更高的索引(我用FTS得到的时间差不多是3.55秒).这是元数据如何影响优化器的典型示例.

顺便说一句,这显然是一个10g或11g的数据库,因为它使用HASH GROUP BY算法,而不是旧的SORT(GROUP BY)算法.