【问题标题】:Oracle linguistic index not used when SQL contains parameter with LIKE当 SQL 包含带有 LIKE 的参数时,不使用 Oracle 语言索引
【发布时间】:2015-03-27 13:00:28
【问题描述】:

我的架构(简化):

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 Enterprise Edition Release 11.2.0.3.0 - 64bit。

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

【问题讨论】:

标签: oracle performance indexing linguistics


【解决方案1】:

根据 Oracle 支持说明 1451804.1,这是将 LIKE 与基于 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') )

这些原始值计算为spdj1501270spdj1501271;这些是从您的常量字符串派生的,并且与您的类似条件匹配的任何值都将在该范围内。该解析时转换必须基于一个常量值,并且不适用于绑定变量或表达式,可能是因为它的评估太晚了。

有关详细信息,请参阅注释,但不幸的是,似乎没有解决方法。您可能必须回到您的 NLS_UPPER 方法。


前面的解释普遍适用,但不适用于这种特殊情况,但留作参考......

一般来说,使用固定值,优化器可以在解析查询时估计查询的选择性,因为它可以大致知道索引值与该值匹配的比例。它可能使用也可能不使用索引,这取决于您使用的实际值。

使用绑定变量,它通过bind variable peeking 提出一个计划:

在绑定变量窥视(也称为绑定窥视)中,优化器在数据库执行语句的硬解析时查看绑定变量中的值。

当查询使用文字时,优化器可以使用文字值来找到最佳计划。但是,当查询使用绑定变量时,优化器必须选择最好的计划,而 SQL 文本中不存在文字。这项任务可能非常困难。通过查看绑定值,优化器可以确定 WHERE 子句条件的选择性,就好像使用了文字一样,从而改进了计划。

它使用收集到的统计数据来确定某个特定值是否比其他值更有可能。这里可能不会出现这种情况,尤其是like。它正在回退到全表扫描,因为它无法确定何时进行硬解析,索引在大多数情况下将更具选择性。例如,想象一下,解析器决定使用索引,但随后您提供的绑定值仅为S,甚至为 null - 使用索引将比全表扫描做更多的工作。

另外值得注意的是:

在选择计划时,优化器仅在硬解析期间查看绑定值。对于所有可能的值,该计划可能不是最优的。

Adaptive cursor sharing 可以缓解这种情况,但此查询可能不符合条件:

优化器用来决定游标是否对绑定敏感的标准包括:

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

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

当我用少量有限数据对此进行模拟时,v$sqlis_bind_sensitiveis_bind_aware 报告为'N'

【讨论】:

  • 谢谢,这很有道理。那么可以做些什么呢?不使用绑定变量?这似乎是错误的......
  • @VinceJS - 这可能取决于您实际执行的查询类型 - 例如,如果 :LOC_REF_NO 始终是固定长度,或者您的实际值有多长 - 您已经定义它是 100 个字符,但它们真的那么长吗?并且您的一般数据传播。不确定是否有一个简单的答案,这取决于您的数据和要求。
  • 奇怪的是,如果我使用带等号的绑定变量,则使用索引。这指出了 LIKE 而不是绑定变量的问题。
  • @VinceJS - 使用 equals 它仍然知道它的选择性 - 例如,它大致知道任何值可能与唯一值有多接近,例如。所以它可以猜测索引可能会比全扫描更有效。
  • 请参阅上面的更新 2。为什么 Oracle 在使用语言索引时不知道索引的选择性,但在使用功能索引时却知道?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-08
  • 2018-12-16
  • 2015-11-01
相关资源
最近更新 更多