当SQL包含LIKE参数时,不使用Oracle语言索引

时间:2015-01-28 15:00:59

标签: oracle performance indexing linguistics

我的架构(简化):

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优化器决定不使用该索引。

1 个答案:

答案 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可以缓解此问题,但此查询可能不符合条件:

  

优化程序用于确定游标是否是绑定敏感的标准包括以下内容:

     
      
  • 优化程序已查看绑定值以生成选择性估计值。

  •   
  • 包含绑定值的列上存在直方图。

  •   

当我使用少量有限数据进行模拟时,Sv$sqlis_bind_sensitive报告为is_bind_aware