【问题标题】:Why is the NLSSORT index not used for this query?为什么此查询不使用 NLSSORT 索引?
【发布时间】:2019-03-19 20:24:09
【问题描述】:

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

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');

【问题讨论】:

    标签: oracle


    【解决方案1】:

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

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

    解释计划显示了 real 表达式。以下是 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'))
    

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

    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 的异常行为。

    【讨论】:

      【解决方案2】:

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

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

      通过将会话的 NLS_COMP 参数更改为 ANSI 并将 NLS_SORT 参数更改为 BINARY_CI,Oracle 会自动将 NLSSORT 函数放入查询中的字符串!在这种情况下,您不必更改查询,因为 Oracle 在幕后为您完成了这项工作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-06-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-10
        • 1970-01-01
        相关资源
        最近更新 更多