使用' LIKE'通过连接,Oracle不使用索引,而当谓词作为常量字符串给出时就是这样。
我从一个简单的表TMP_RSQ_FEATURE开始,带有一个整数和字符串列。它有137,000行。它在FEATURE_NAME列上有一个名为TMP_R_FN的索引。
select dbms_metadata.get_ddl('TABLE', 'TMP_RSQ_FEATURE') from dual;
DBMS_METADATA.GET_DDL('TABLE','TMP_RSQ_FEATURE')
------------------------------------------------------------------
CREATE TABLE TMP_RSQ_FEATURE
( "FEATURE_ID" NUMBER(8,0) NOT NULL ENABLE,
"FEATURE_NAME" VARCHAR2(100) NOT NULL ENABLE
) SEGMENT CREATION IMMEDIATE
select dbms_metadata.get_ddl('INDEX', 'TMP_R_FN') from dual;
DBMS_METADATA.GET_DDL('INDEX','TMP_R_FN')
------------------------------------------------------------------
CREATE INDEX TMP_R_FN ON TMP_RSQ_FEATURE ("FEATURE_NAME")
...
当我为LIKE谓词使用常量字符串时,查询是有效的,并且计划显示通过索引的访问:
select feature_id, feature_name
from tmp_rsq_feature f
where f.feature_name like 'CD7%';
FEATURE_ID FEATURE_NAME
---------- ---------------------------------
2442627 CD7
2435077 CD70
2448695 CD72
2472766 CD74
2491509 CD79A
2442159 CD79B
6 rows selected.
Elapsed: 00:00:00.06
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 17 | 4 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| TMP_RSQ_FEATURE | 1 | 17 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | TMP_R_FN | 1 | | 2 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("F"."FEATURE_NAME" LIKE 'CD7%')
filter("F"."FEATURE_NAME" LIKE 'CD7%')
但是,当我尝试通过带有单行的参数表提供谓词时:
select * from spf_param;
VAL
-----------------------------------
CD7%
...并通过连接该字段的EXISTS条件提供谓词,查询速度慢10倍,并且不使用索引。
select feature_id, feature_name
from tmp_rsq_feature f where exists
(select 0 from spf_param p where f.feature_name like p.val);
FEATURE_ID FEATURE_NAME
---------- ----------------------------------
2491509 CD79A
2448695 CD72
2442159 CD79B
2442627 CD7
2472766 CD74
2435077 CD70
6 rows selected.
Elapsed: 00:00:00.73
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 17 | 396K (1)| 00:00:33 |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS FULL| TMP_RSQ_FEATURE | 137K| 2284K| 120 (2)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| SPF_PARAM | 1 | 5 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter( EXISTS (SELECT 0 FROM "SPF_PARAM" "P" WHERE :B1 LIKE
"P"."VAL"))
3 - filter(:B1 LIKE "P"."VAL")
这似乎令人惊讶。首先,CBO知道SPF_PARAM只有一行。虽然它可能没有对该行的内容做任何假设,但非选择性的假设似乎表明它假设最坏,该列只是“#”;'%'。这似乎是一个过于保守的假设。是否有任何方法可以诱使CBO对SPF_PARAM的内容做出更强有力的假设?