【问题标题】:Using NOT LIKE in Oracle在 Oracle 中使用 NOT LIKE
【发布时间】:2020-05-24 15:50:35
【问题描述】:

这可能是一个愚蠢的问题——我希望不是——但我仍然不明白为什么这段代码不在 Oracle 中运行,而是在 MS SQL Server 中运行。

代码是:

SELECT DISTINCT CITY FROM STATION
WHERE CITY NOT LIKE '[AEIOUaeiou]%'
ORDER BY CITY ASC;

代码在 SQL Server 中给了我所有不以元音开头的城市,但是当我在 Oracle 中运行它时,它给了我所有的城市。

这是为什么呢?

【问题讨论】:

  • 未显示查询。NOT LIKE 是那些“标准”运算符之一,因此结果可能取决于关键(且不包括)上下文,包括数据。
  • 当你说“为什么这段代码不运行”时你想说什么?你得到的结果不一样?
  • 我想要所有不以元音开头的城市。在 Oracle 中,我只给了我所有的城市。

标签: sql sql-server regex string oracle


【解决方案1】:

SQL Server 支持LIKE 模式中的一小部分正则表达式。 Oracle 没有,但它通过regexp_* 函数提供全面的正则表达式支持。

这里可以使用regexp_like():

SELECT DISTINCT CITY FROM STATION
WHERE NOT REGEXP_LIKE(CITY, '^[aeiou]', 'i')
ORDER BY CITY ASC;

正则表达式^[aeiou] 表示:字符串开头列出的字符之一('^' 代表)。第三个参数称为 match 参数'i' 使搜索不区分大小写。

【讨论】:

  • 在 SQL Server 中,LIKE 运算符可以很好地与索引配合使用(前提是它是兼容的索引,例如,使用 LIKE 运算符进行排序兼容(即不区分大小写)前缀字符串搜索) - Oracle 中的 regexp_like 函数是否支持相同的功能 - 或者它总是会进行表扫描?
  • @Dai - Oracle 中的 LIKE 运算符(尽管它更受限制)也适用于索引,这很有意义。正则表达式不会——我相信在所有数据库中都是如此。但是,在这种情况下,问题本身(关于索引)没有多大意义。在此主题的我的回复下查看我对您的回答。
【解决方案2】:

您的尝试不起作用,因为在 Oracle 中,条件 LIKE / NOT LIKE 适用于正则表达式。取而代之的是,只有两个(或三个)元字符:%(您正试图正确使用)、恰好代表一个字符的下划线以及可能的转义字符(如果您需要转义% 或 _),必须在 LIKE / NOT LIKE 条件中声明。其他一切都是文字字符。没有城市名称以子字符串 [AEIOuaeiou](12 个字符)开头。 是您的病情所阻止的;因此,事实上,它并没有阻止任何名称包含在输出中。

GMB 为您提供了 100% 正确的解决方案,本着您的问题的精神 - 使用正则表达式。

我只知道Oracle,没有其他数据库产品;但至少在 Oracle 中,标准字符串函数和条件比它们的正则表达式对应物快得多,并且应该尽可能使用。至少在性能很重要的时候;我相信尽可能使用它们是一种很好的做法,即使性能很重要,只是为了养成良好的习惯。

在这里,仅使用标准字符串函数的简单有效的解决方案是绝对可能的,并且相对简单。

select city
from   station
where  substr(city, 1, 1) not in ('A', 'E', 'I', 'O', 'U', 'a', 'e', 'i', 'o', 'u')
;

当然,这可以通过各种方式进行修改;例如,我们可以使用lower() 来避免重复大写和小写元音。或者,甚至:

select city
from   station
where  'aeiou' not like '%' || lower(substr(city, 1, ,1)) || '%'
;

但我认为这样的代码(即使有时它可能会稍微快一点)是噱头;最好让代码准确地说明您的意思,就像在第一个版本中一样。无论如何,我不确定性能是否存在差异。 (但是这两个版本都应该比使用正则表达式的任何版本都快得多,因为无论问题多么简单,正则表达式都使用了一种重要的机制。)

请注意,我在select 子句中省略了“distinct”,因为它与问题完全无关。始终尝试使问题尽可能“小”,同时仍能充分反映实际提出的问题。

【讨论】:

  • SARGable 如何在 Oracle 中使用 substr
  • @Dai - 由于多种原因,您的问题没有意义。第一:OP的查询只选择city列,过滤器也只在city上;因此,如果该列上有索引,则查询确实只使用索引,因为索引已经包含所有必要的数据。 (注意,NOT LIKE 过滤器意味着 null 的“值”对于 city 无论如何都会被过滤掉 - 所以索引确实有所有需要的数据。)其次,如果查询需要选择其他列,它过滤器不太可能有足够的选择性来证明通过索引访问是合理的
  • @Dai - 此外,索引访问在 NOT LIKE 条件下不太适用。优化器不知道 city 只能以字母开头(即使知道,该字母也可能来自非 ASCII 字母);因此,即使是更新版本的 Oracle,其中基于 OR 的过滤器(... LIKE 'B' 或 ... LIKE 'C' 或 ... )也不一定是显示停止器,不知道如何访问通过 NOT LIKE 条件上的索引。
【解决方案3】:
WHERE CITY NOT LIKE '[AEIOUaeiou]%'

代码在 SQL Server 中给了我所有不以元音开头的城市,但是当我在 Oracle 中运行它时,它给了我所有的城市。

这是为什么呢?

您的问题的答案是 SQL Server 扩展了 LIKE 的功能以包含字符范围——遗憾的是,它忽略了正则表达式的许多其他有用之处。好吧,实际上,SQL Server 并没有这样做。 Sybase 做到了,微软从 Sybase 购买了代码库。所以这两个数据库支持这种扩展的LIKE 功能。 (我可以补充一点,MS Access 也有类似的东西。)

大多数其他数据库真正支持正则表达式,通过函数支持(例如 Oracle 中的 regexp_like() 或 Postgres 中的 '~' 和 MySQL 中的 regexp 等运算符)。他们对待LIKE 模式(例如'[AEIOUaeiou]%')的处理方式与它所说的完全一样:他们将匹配以12 个特定字符开头的字符串——'[''A' 等等。因为没有城市名称以[ 开头,更不用说后面是十个元音,所以没有城市与该模式匹配——并且都通过了NOT LIKE 逻辑。

此外,在 SQL Server 中,比较通常不区分大小写,因此 '[A]' 将匹配 'A''a'。您可以简化该数据库中的表达式。另一方面,Oracle 中的比较通常区分大小写,因此您需要小心。

我认为 GMB 已经解释了如何使用正则表达式来做你想做的事。

【讨论】:

    猜你喜欢
    • 2017-05-17
    • 2015-08-23
    • 2012-06-28
    • 1970-01-01
    • 2012-10-11
    • 1970-01-01
    • 2013-09-21
    • 2010-11-17
    • 2019-08-25
    相关资源
    最近更新 更多