为什么NLSSORT索引不用于此查询?

时间:2013-12-12 01:47:38

标签: oracle

在我们的应用程序中,我们在会话级别配置了不区分大小写的语义:

alter session set NLS_COMP=LINGUISTIC;
alter session set NLS_SORT=BINARY_AI;

但是我希望有一个带有二进制语义的NAME列的表,所以我相应地定义了一个基于函数的索引:

create table RAW_SCREEN (
   ID   number(10)     constraint RSCR_PK primary key,
   NAME nvarchar2(256) not null
);
create unique index RSCR_IDX on RAW_SCREEN (nlssort(NAME, 'NLS_SORT=BINARY'));

我希望下面的查询能够利用基于函数的索引:

select * from RAW_SCREEN where 
    nlssort(NAME, 'NLS_SORT=BINARY') = nlssort(N'raw_screen1', 'NLS_SORT=BINARY');

但事实并非如此。查询计划显示表扫描。在进行实验时,我发现NAME上的一个简单索引可以解决问题:

create unique index RSCR_IDX2 on RAW_SCREEN (NAME);

再次运行查询时,RSCR_IDX2索引已成功使用。

现在,这并不奇怪,但我无法理解为什么优化器没有使用第一个基于函数的索引。索引表达式与WHERE条件中使用的表达式完全匹配。你知道它为什么没用过吗?

注意:这是在Oracle 10.2上运行的

如果您想尝试一下,这是一个完整的测试脚本:

alter session set NLS_COMP=LINGUISTIC;
alter session set NLS_SORT=BINARY_AI;

create table RAW_SCREEN (
   ID                   number(10)            constraint RSCR_PK primary key,
   NAME                 nvarchar2(256)        not null
);

create unique index RSCR_IDX on RAW_SCREEN (nlssort(NAME, 'NLS_SORT=BINARY'));
--create unique index RSCR_IDX2 on RAW_SCREEN (NAME);

begin
  for i in 1..10000
  loop
    insert into RAW_SCREEN values (i, 'raw_screen' || i);
  end loop;
end;
/
commit;

select * from RAW_SCREEN where nlssort(NAME, 'NLS_SORT=BINARY') = nlssort(N'raw_screen1000', 'NLS_SORT=BINARY');

2 个答案:

答案 0 :(得分:2)

表达式在DML中转换为NLS会话设置,但在DDL中不转换。

这可以说是NLSSORT(char, 'NLS_SORT=BINARY')行为的错误 从the manual:“如果指定BINARY,则此函数返回char。” 但是索引的为真。通常,索引表达式不经历任何转换是非常方便的;如果它取决于会话设置 比DBMS_METADATA.GET_DDL这样的工具必须返回许多alter session语句。但在这种情况下,这意味着您可以创建永远不会的索引 使用。

解释计划显示真实的表达式。以下是Oracle在未明确使用的情况下在会话中使用nlssort的方式:

alter session set nls_comp=linguistic;
alter session set nls_sort=binary_ai;
drop table raw_screen;
create table raw_screen (
   id   number(10)     constraint rscr_pk primary key,
   name nvarchar2(256) not null
);
create unique index idx_binary_ai
      on raw_screen (nlssort(name, 'nls_sort=binary_ai'));
explain plan for select * from raw_screen where name = n'raw_screen1000';
select * from table(dbms_xplan.display(format=>'basic predicate'));

Plan hash value: 2639454581

-----------------------------------------------------
| Id  | Operation                   | Name          |
-----------------------------------------------------
|   0 | SELECT STATEMENT            |               |
|   1 |  TABLE ACCESS BY INDEX ROWID| RAW_SCREEN    |
|*  2 |   INDEX UNIQUE SCAN         | IDX_BINARY_AI |
-----------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access(NLSSORT("NAME",'nls_sort=''BINARY_AI''')=HEXTORAW('0072006
              10077005F00730063007200650065006E003100300030003000'))

此示例显示DML删除了nlssort(char, 'nls_sort=binary')

alter session set nls_comp=linguistic;
alter session set nls_sort=binary_ai;
drop table raw_screen;
create table raw_screen (
   id   number(10)     constraint rscr_pk primary key,
   name nvarchar2(256) not null
);
create unique index idx_binary_ai on
      raw_screen (nlssort(name, 'nls_sort=binary_ai'));
explain plan for select * from raw_screen where
  nlssort(name,'nls_sort=binary') = nlssort(N'raw_screen1000','nls_sort=binary');
select * from table(dbms_xplan.display(format=>'basic predicate'));

Plan hash value: 237065300

----------------------------------------
| Id  | Operation         | Name       |
----------------------------------------
|   0 | SELECT STATEMENT  |            |
|*  1 |  TABLE ACCESS FULL| RAW_SCREEN |
----------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("NAME"=U'raw_screen1000')

总之 - 索引DDL需要与转换的表达式完全匹配,这可能取决于会话设置和binary的异常行为。

答案 1 :(得分:0)

当您将函数应用于查询的where子句中的列时,此列上的任何对应索引也必须包含该函数,Oracle才能在执行查询时使用它们。如果适当地设置了NLS_COMP和NLS_SORT,则NLSSORT函数可以应用于where子句中的字符串,该字符串自动为Oracle。

要启用不区分大小写的搜索,我们必须通过应用诸如upper(),lower()等函数来转换存储在表中的字符串。然后,我们还必须在列上创建基于函数的索引,并使用与查询中使用的功能相同。

通过将NLS_COMP参数更改为ANSI并将NLS_SORT参数更改为BINARY_CI进行会话,Oracle会自动将NLSSORT函数放置到查询中的字符串上!在这种情况下,您不必更改查询,因为Oracle会在后台为您执行此操作。