【发布时间】:2011-10-03 07:34:25
【问题描述】:
我有一个索引:
CREATE INDEX BLAH ON EMPLOYEE(SUBSTR(TO_CHAR(EMPSHIRTNO), 1, 4));
和一个 SQL 语句:
SELECT COUNT(*)
FROM (SELECT COUNT(*)
FROM EMPLOYEE
GROUP BY SUBSTR(TO_CHAR(EMPSHIRTNO), 1, 4)
HAVING COUNT(*) > 100);
但除非我添加提示,否则它会继续执行全表扫描而不是使用索引。
EMPSHIRTNO 不是主键,EMPNO 是(此处未使用)。
复杂查询
EXPLAIN PLAN FOR SELECT COUNT(*) FROM (SELECT COUNT(*) FROM EMPLOYEE
GROUP BY SUBSTR(TO_CHAR(EMPSHIRTNO), 1, 4)
HAVING COUNT(*) > 100);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1712471557
----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 24 (9)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | | |
| 2 | VIEW | | 497 | | 24 (9)| 00:00:01 |
|* 3 | FILTER | | | | | |
----------------------------------------------------------------------------------
| 4 | HASH GROUP BY | | 497 | 2485 | 24 (9)| 00:00:01 |
| 5 | TABLE ACCESS FULL| EMPLOYEE | 9998 | 49990 | 22 (0)| 00:00:01||
----------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter(COUNT(*)>100)
17 rows selected.
ANALYZE INDEX BLAH VALIDATE STRUCTURE;
SELECT BTREE_SPACE, USED_SPACE FROM INDEX_STATS;
BTREE_SPACE USED_SPACE
----------- ----------
176032 150274
简单查询:
EXPLAIN PLAN FOR SELECT * FROM EMPLOYEE;
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 2913724801
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9998 | 439K| 23 (5)| 00:00:01 |
| 1 | TABLE ACCESS FULL| EMPLOYEE | 9998 | 439K| 23 (5)| 00:00:01 |
------------------------------------------------------------------------------
8 rows selected.
可能是因为 NOT NULL 约束是通过 CHECK 约束强制执行的,而不是最初在表创建语句中定义的?当我这样做时它将使用索引:
SELECT * FROM EMPLOYEE WHERE SUBSTR(TO_CHAR(EMPSHIRTNO), 1, 4) = '1234';
对于那些建议它无论如何都需要读取所有行的人(我认为它不会在计数时这样做),索引也不会用于此:
SELECT SUBSTR(TO_CHAR(EMPSHIRTNO), 1, 4) FROM EMPLOYEE;
事实上,在 EMPSHIRTNO 上放置一个索引并执行 SELECT EMPSHIRTNO FROM EMPLOYEE;也不使用索引。我应该指出,EMPSHIRTNO 不是唯一的,表中有重复。
【问题讨论】:
-
这不是一个有效的查询。它的左括号多于右括号。
-
请显示两个查询计划(有和没有提示)。您最近是否更新了数据库统计信息?
-
@Codo 出于某种原因,我什至无法让它使用带有提示的索引。我已经更新了统计数据。我认为问题在于查询不使用 where SUBSTR.... 子句,因此数据库不想使用索引。
-
也许甲骨文比你聪明? (开个玩笑),但 FTS 并不总是一件坏事。在这种情况下,做 FTS 可能会更快/更好
-
Oracle 很可能无法确定函数 SUBSTR(TO_CHAR(xx), 1, 4) 的结果是否为 NOT NULL。所以它假设它是可空的,因此索引不包含所有行。所以它不能也不会使用它。