【问题标题】:Case insensitive searching in OracleOracle 中不区分大小写的搜索
【发布时间】:2011-07-20 10:47:40
【问题描述】:

LIKE 和其他比较运算符 = 等的默认行为区分大小写。

是否可以让它们不区分大小写?

【问题讨论】:

  • 友情提示,即使在 user_name 上有索引,某些示例搜索也会导致全表扫描。
  • 你考虑过用REGEXP_LIKE(username,'me','i')代替LIKE吗?
  • 不,LIKE 适合我

标签: sql oracle case-sensitive case-insensitive sql-like


【解决方案1】:

在不使用全文索引的情况下,有 3 种主要方法可以在 Oracle 中执行不区分大小写的搜索。

最终选择哪种方法取决于您的个人情况;要记住的主要事情是,为了提高性能,您必须正确索引以进行不区分大小写的搜索。

1。大小写相同的列和字符串。

您可以使用UPPER()LOWER() 强制所有数据大小写相同:

select * from my_table where upper(column_1) = upper('my_string');

select * from my_table where lower(column_1) = lower('my_string');

如果column_1 未在upper(column_1)lower(column_1) 上建立索引,则可能会强制执行全表扫描。为了避免这种情况,您可以创建一个function-based index

create index my_index on my_table ( lower(column_1) );

如果您使用 LIKE,则必须在要搜索的字符串周围连接一个 %

select * from my_table where lower(column_1) LIKE lower('my_string') || '%';

This SQL Fiddle 演示了所有这些查询中发生的情况。请注意解释计划,它指示何时使用索引,何时不使用。

2。使用正则表达式。

从 Oracle 10g 开始,REGEXP_LIKE() 可用。您可以指定_match_parameter_'i',以执行不区分大小写的搜索。

为了将其用作等式运算符,您必须指定字符串的开始和结束,用克拉和美元符号表示。

select * from my_table where regexp_like(column_1, '^my_string$', 'i');

为了执行 LIKE 的等效操作,可以删除这些。

select * from my_table where regexp_like(column_1, 'my_string', 'i');

请注意这一点,因为您的字符串可能包含正则表达式引擎会以不同方式解释的字符。

This SQL Fiddle 向您显示相同的示例输出,但使用 REGEXP_LIKE()。

3。在会话级别更改它。

NLS_SORT 参数控制排序的排序规则和各种比较运算符,包括 = 和 LIKE。您可以通过更改会话来指定二进制、不区分大小写的排序。这意味着在该会话中执行的每个查询都将执行不区分大小写的参数。

alter session set nls_sort=BINARY_CI

如果您想指定不同的语言,或使用 BINARY_AI 进行不区分重音的搜索,还有很多关于 linguistic sorting and string searching 的附加信息。

您还需要更改NLS_COMP 参数;引用:

遵循 NLS_SORT 参数的确切运算符和查询子句 取决于 NLS_COMP 参数的值。如果操作员或 子句不服从由 NLS_COMP 确定的 NLS_SORT 值, 使用的排序规则是 BINARY。

NLS_COMP 的默认值为 BINARY;但是,LINGUISTIC 规定 Oracle 应该注意 NLS_SORT 的值:

WHERE 子句和 PL/SQL 中所有 SQL 操作的比较 块应使用 NLS_SORT 中指定的语言排序 范围。为了提高性能,您还可以定义一个 您想要语言的列上的语言索引 比较。

所以,再一次,您需要更改会话

alter session set nls_comp=LINGUISTIC

如文档中所述,您可能需要创建 linguistic index 以提高性能

create index my_linguistc_index on my_table 
   (NLSSORT(column_1, 'NLS_SORT = BINARY_CI'));

【讨论】:

  • “创建一个基于函数的索引”这可以带来多么神奇的变化
  • 请问为什么select * from my_table where lower(column_1) LIKE lower('my_string') || '%';select * from my_table where lower(column_1) LIKE lower('my_string%'); 不同?它有什么好处吗?
  • 一个原因是,如果您的查询是参数化的(可能在大多数情况下),那么您的调用代码不需要总是在末尾连接一个 % @lopezvit。
  • 如果有一些字符会弄乱regexp_like的结果,有没有办法转义这些字符串?举个例子,如果字符串有 $,输出将不是我们期望的。 //cc @Ben 和其他人请分享。
  • `is the escape character@bozzmob。如果正则表达式操作的字符串包含$,则输出应该没有区别,如果您在正则表达式中需要$ 文字,这可能只会给您带来问题。如果您有特定问题,如果此评论/答案没有帮助,我会问另一个问题。
【解决方案2】:

从 10gR2 开始,Oracle 允许通过设置 NLS_COMPNLS_SORT 会话参数来微调字符串比较的行为:

SQL> SET HEADING OFF
SQL> SELECT *
  2  FROM NLS_SESSION_PARAMETERS
  3  WHERE PARAMETER IN ('NLS_COMP', 'NLS_SORT');

NLS_SORT
BINARY

NLS_COMP
BINARY


SQL>
SQL> SELECT CASE WHEN 'abc'='ABC' THEN 1 ELSE 0 END AS GOT_MATCH
  2  FROM DUAL;

         0

SQL>
SQL> ALTER SESSION SET NLS_COMP=LINGUISTIC;

Session altered.

SQL> ALTER SESSION SET NLS_SORT=BINARY_CI;

Session altered.

SQL>
SQL> SELECT *
  2  FROM NLS_SESSION_PARAMETERS
  3  WHERE PARAMETER IN ('NLS_COMP', 'NLS_SORT');

NLS_SORT
BINARY_CI

NLS_COMP
LINGUISTIC


SQL>
SQL> SELECT CASE WHEN 'abc'='ABC' THEN 1 ELSE 0 END AS GOT_MATCH
  2  FROM DUAL;

         1

您还可以创建不区分大小写的索引:

create index
   nlsci1_gen_person
on
   MY_PERSON
   (NLSSORT
      (PERSON_LAST_NAME, 'NLS_SORT=BINARY_CI')
   )
;

此信息取自 Oracle case insensitive searches。文章提到了REGEXP_LIKE,但它似乎也适用于旧的=


在早于 10gR2 的版本中,它实际上无法完成,如果您不需要 accent-insensitive 搜索,通常的方法是只需 UPPER() 列和搜索表达。

【讨论】:

  • 这很好用,但它使使用 LIKE / = 运算符的更新非常慢...... :(
  • @SaqibAli Arbitrary LIKE 表达式(例如WHERE foo LIKE '%abc%')如果无法被索引已经足够慢了,我认为这与区分大小写无关。
  • 你也可以在 SQLPLUS 之外设置这些,就像在 shell 环境中一样。例如在使用DBD::Oracle 的Perl 脚本中,您可以在调用`DBI->connect` 之前编写$ENV{NLS_SORT} = 'BINARY_CI'; $ENV{NLS_COMP} = 'LINGUISTIC';
  • 嘿,ALTER SESSION 是否只会更改您的本地更正实例,这是否意味着像您当前的会话一样,即如果我关闭并重新打开它会重置。有没有一种方法可以让我看到当前值是什么,这样如果它在任何地方都存在,我可以改回原始设置...
【解决方案3】:

也许你可以尝试使用

SELECT user_name
FROM user_master
WHERE upper(user_name) LIKE '%ME%'

【讨论】:

  • 输入参数全大写时有效,小写或混合时无效
  • 那你有没有想过WHERE upper(user_name) LIKE UPPER('%ME%')? :)
  • @sergionni 您也必须将搜索词大写!
  • @sergionni,那你为什么不在输入参数上也使用UPPER
  • @V4Vendetta 使用upper 函数会丢失索引,您知道如何使用索引进行搜索吗?
【解决方案4】:

从 Oracle 12c R2 你可以使用COLLATE operator:

COLLATE 运算符确定表达式的排序规则。此运算符使您能够覆盖数据库使用标准排序规则派生规则为表达式派生的排序规则。

COLLATE 运算符采用一个参数 collat​​ion_name,您可以为其指定命名排序规则或伪排序规则。如果排序规则名称包含空格,则必须用双引号将名称括起来。

演示:

CREATE TABLE tab1(i INT PRIMARY KEY, name VARCHAR2(100));

INSERT INTO tab1(i, name) VALUES (1, 'John');
INSERT INTO tab1(i, name) VALUES (2, 'Joe');
INSERT INTO tab1(i, name) VALUES (3, 'Billy'); 
--========================================================================--
SELECT /*csv*/ *
FROM tab1
WHERE name = 'jOHN' ;
-- no rows selected

SELECT /*csv*/ *
FROM tab1
WHERE name COLLATE BINARY_CI = 'jOHN' ;
/*
"I","NAME"
1,"John"
*/

SELECT /*csv*/ *
FROM tab1 
WHERE name LIKE 'j%';
-- no rows selected

SELECT /*csv*/ *
FROM tab1 
WHERE name COLLATE BINARY_CI LIKE 'j%';
/*
"I","NAME"
1,"John"
2,"Joe"
*/

db<>fiddle demo

【讨论】:

    【解决方案5】:
    select user_name
    from my_table
    where nlssort(user_name, 'NLS_SORT = Latin_CI') = nlssort('%AbC%', 'NLS_SORT = Latin_CI')
    

    【讨论】:

    • 第二个NLSSORT 的第一个参数中的% 不是 是通配符,对吧?他们有点困惑。
    【解决方案6】:

    你可以这样做:

    where regexp_like(name, 'string$', 'i');
    

    【讨论】:

      【解决方案7】:

      如果将 COLLATE 运算符放在表达式的末尾,它也可以工作,这对我来说似乎更清晰。 所以你可以使用这个:

      WHERE name LIKE 'j%' COLLATE BINARY_CI 
      

      而不是这个:

      WHERE name COLLATE BINARY_CI LIKE 'j%'
      

      总之,我喜欢 COLLATE 运算符解决方案,原因如下:

      • 在表达式中只放一次,不用担心多个 UPPER 或 LOWER,以及放在哪里
      • 它与您需要它的确切语句和表达式隔离,这与使其适用于所有事物的 ALTER SESSION 解决方案不同。无论数据库或会话 NLS_SORT 设置如何,您的查询都将始终如一地工作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-09-15
        • 2011-11-20
        • 2013-09-26
        • 2023-03-20
        • 1970-01-01
        • 2010-10-13
        相关资源
        最近更新 更多