我有一个查询,当我想到它时没有使用索引,所以我出于好奇而转载它:
创建一个包含1.000.000行的test_table
(col
中的10个不同值,some_data
中的500字节数据)。
CREATE TABLE test_table AS (
SELECT MOD(ROWNUM,10) col, LPAD('x', 500, 'x') some_data
FROM dual
CONNECT BY ROWNUM <= 1000000
);
创建索引并收集表统计信息:
CREATE INDEX test_index ON test_table ( col );
EXEC dbms_stats.gather_table_stats( 'MY_SCHEMA', 'TEST_TABLE' );
尝试获取col
和COUNT
:
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
---------------------------------------------------------------------------------
未使用索引,前提是提示不会更改此内容。
我猜,在这种情况下不能使用索引,但为什么呢?
答案 0 :(得分: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
如果优化器确定不使用索引更有效(可能是由于重写查询),那么它不会。优化器提示就是这样,即提示告诉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
答案 1 :(得分: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
--------------------------------------------------------------------------------
答案 2 :(得分: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>
之所以重要,是因为NULL值不包含在普通的B-TREE索引中,但GROUP BY必须在查询中包含NULL作为分组“值”。通过告诉优化器col
中没有NULL,它可以自由地使用效率更高的索引(我用FTS得到的时间差不多是3.55秒)。这是元数据如何影响优化器的典型示例。
顺便说一句,这显然是10g或11g数据库,因为它使用HASH GROUP BY算法,而不是旧的SORT(GROUP BY)算法。
答案 3 :(得分:0)
位图索引也可以
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 | | | | | ---------------------------------------------------------------------------------------