我的架构(简化):
CREATE TABLE LOC
(
LOC_ID NUMBER(15,0) NOT NULL,
LOC_REF_NO VARCHAR2(100 CHAR) NOT NULL
)
/
CREATE INDEX LOC_REF_NO_IDX ON LOC
(
NLSSORT("LOC_REF_NO",'nls_sort=''BINARY_AI''') ASC
)
/
我的查询(在SQL * Plus中):
ALTER SESSION SET NLS_COMP=LINGUISTIC NLS_SORT=BINARY_AI
/
VAR LOC_REF_NO VARCHAR2(50)
BEGIN
:LOC_REF_NO := 'SPDJ1501270';
END;
/
-- Causes full table scan (i.e, does not use LOC_REF_NO_IDX)
SELECT * FROM LOC WHERE LOC_REF_NO LIKE :LOC_REF_NO||'%';
-- Causes index scan (i.e. uses LOC_REF_NO_IDX)
SELECT * FROM LOC WHERE LOC_REF_NO LIKE 'SPDJ1501270%';
通过执行AUTOTRACE(EXPLAIN PLAN)已确认索引未使用,并且SQL运行速度较慢。尝试了一些没有成功的事情。任何人都知道发生了什么事?我使用的是Oracle Database 11g企业版11.2.0.3.0版 - 64位。
更新1 :
请注意,当我使用带参数的等号时使用索引:
SELECT * FROM LOC WHERE LOC_REF_NO = :LOC_REF_NO;
解释计划:
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 93 | 5 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| LOC | 1 | 93 | 5 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | LOC_REF_NO_IDX | 1 | | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(NLSSORT("LOC_REF_NO",'nls_sort=''BINARY_AI''')=NLSSORT(:LOC_REF_NO,'nls_
sort=''BINARY_AI'''))
尽管
SELECT * FROM LOC WHERE LOC_REF_NO LIKE :LOC_REF_NO||'%';
解释计划:
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 50068 | 3471K| 5724 (1)| 00:01:09 |
|* 1 | TABLE ACCESS FULL| LOC | 50068 | 3471K| 5724 (1)| 00:01:09 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("LOC_REF_NO" LIKE :LOC_REF_NO||'%')
傻眼!
更新2 :
我们在索引上使用NLSSORT的原因是使Oracle查询不区分大小写,这是一般建议。以前我们使用NLS_UPPER的功能索引。奇怪的是,索引始终使用,参数与否,如下所示。
因此,如果表格如上所述,则删除了LOC_REF_NO_IDX索引,并添加了以下内容:
CREATE INDEX LOC_REF_NO_CI_IDX ON LOC
(
NLS_UPPER(LOC_REF_NO) ASC
)
/
以下所有内容都使用索引:
ALTER SESSION SET NLS_COMP=BINARY NLS_SORT=BINARY;
SELECT * FROM LOC WHERE NLS_UPPER(LOC_REF_NO) LIKE :LOC_REF_NO||'%';
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 50068 | 5329K| 5700 (1)| 00:01:09 |
| 1 | TABLE ACCESS BY INDEX ROWID| LOC | 50068 | 5329K| 5700 (1)| 00:01:09 |
|* 2 | INDEX RANGE SCAN | LOC_REF_NO_CI_IDX | 9012 | | 43 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(NLS_UPPER("LOC_REF_NO") LIKE :LOC_REF_NO||'%')
filter(NLS_UPPER("LOC_REF_NO") LIKE :LOC_REF_NO||'%')
因此,出于某种原因,当LIKE与语言索引上的参数一起使用时,Oracle优化器决定不使用该索引。
答案 0 :(得分:3)
根据Oracle支持说明1451804.1,这是使用基于LIKE
的{{1}}索引的已知限制。
如果您查看固定值查询的执行计划,您会看到类似的内容:
NLSSORT
这些原始值的评估结果为Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(NLSSORT("LOC_REF_NO",'nls_sort=''BINARY_AI''')>=HEXTORAW('7370646A313530
3132373000') AND NLSSORT("LOC_REF_NO",'nls_sort=''BINARY_AI''')<HEXTORAW('7370646A313
5303132373100') )
和spdj1501270
;这些是从您的常量字符串派生的,并且符合您的类似条件的任何值都将在该范围内。解析时转换必须基于一个常量值,并且不能使用绑定变量或表达式,大概是因为它的评估太晚了。
请参阅说明以获取更多信息,但遗憾的是,它似乎不是一种解决方法。您可能需要回到spdj1501271
方法。
以前的解释一般适用但不适用于此特定情况,但保留供参考......
通常,使用固定值,优化程序可以估计查询在解析时的选择性,因为它可以大致知道索引值与该值匹配的比例。它可能会也可能不会使用索引,具体取决于您使用的实际值。
使用绑定变量,它通过bind variable peeking:
提出计划在绑定变量peeking(也称为绑定查看)中,当数据库执行语句的硬解析时,优化器会查看绑定变量中的值。
当查询使用文字时,优化程序可以使用文字值来查找最佳计划。但是,当查询使用绑定变量时,优化程序必须选择最佳计划,而SQL文本中不存在文字。这项任务非常困难。通过查看绑定值,优化器可以确定WHERE子句条件的选择性,就像使用了文字一样,从而改进了计划。
它使用它收集的统计数据来确定任何特定值是否比其他值更可能。这可能不是这种情况,尤其是NLS_UPPER
。它回退到全表扫描,因为它无法确定何时进行硬分析,指数在大多数情况下会更具选择性。例如,想象一下,解析决定使用索引,但是你提供的绑定值只有like
,甚至是null - 使用索引会比完整的表扫描做更多的工作。
另外值得注意的是:
在选择计划时,优化程序仅在硬解析期间查看绑定值。对于所有可能的值,此计划可能不是最佳的。
Adaptive cursor sharing可以缓解此问题,但此查询可能不符合条件:
优化程序用于确定游标是否是绑定敏感的标准包括以下内容:
优化程序已查看绑定值以生成选择性估计值。
包含绑定值的列上存在直方图。
当我使用少量有限数据进行模拟时,S
将v$sql
和is_bind_sensitive
报告为is_bind_aware
。