我有一个索引:
CREATE INDEX BLAH ON EMPLOYEE(SUBSTR(TO_CHAR(EMPSHIRTNO), 1, 4));
和SQL STATEMENT:
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;也不使用索引。我应该指出,EMPSHIRTNO并不是唯一的,表中有重复项。
答案 0 :(得分:3)
由于查询的性质,无论如何都需要扫描表格的每一行。所以oracle可能会认为全表扫描是最有效的方法。因为它使用HASH GROUP BY
,所以最终没有像oracle 7天那样令人讨厌的排序。
首先得到每件SUBSTR(...)
衬衫的数量。因此它的查询的第一部分必须扫描整个表
SELECT COUNT(*)
FROM EMPLOYEE
GROUP BY SUBSTR(TO_CHAR(EMPSHIRTNO), 1, 4)
接下来,您要放弃计数为< = 100的SUBSTR(...)
.Oracle需要扫描所有行以验证这一点。从技术上讲,你可以争辩说,一旦它有101它就不再需要了,但我认为Oracle不能解决这个问题,特别是当你问它在子查询的SELECT COUNT(*)
中的总数是多少时
HAVING COUNT(*) > 100);
所以基本上给你的答案是你需要Oracle扫描表中的每一行,所以索引对过滤没有帮助。因为它使用散列组,索引对分组没有任何帮助。因此,使用索引会降低查询速度,这就是Oracle不使用它的原因。
答案 1 :(得分:0)
我认为您可能需要在SUBSTR上构建基于函数的索引(TO_CHAR(EMPSHIRTNO),1,4); SQL中的函数倾向于使列上的常规索引无效。
答案 2 :(得分:0)
我相信@Codo是正确的。 Oracle无法确定表达式将始终为非null,然后必须假定某些空值可能不会 存储在索引中。
(似乎Oracle 应该能够发现表达式不可为空。通常,任何随机SUBSTR表达式总是存在的可能性 not null可能非常低,也许Oracle只是将所有SUBSTR表达式混为一谈?)
您可以使用以下解决方法之一使索引可用于您的查询:
--bitmap index:
create bitmap index blah on employee(substr(to_char(empshirtno), 1, 4));
--multi-column index:
alter table employee add constraint blah primary key (id, empshirtno);
--indexed virtual column:
create table employee(empshirtno varchar2(10) not null
,empshirtno_for_index as (substr(empshirtno,1,4)) not null );
create index blah on employee(empshirtno_for_index);