'~' 和 '^' 如何与 PostgreSQL 中的实际示例一起使用?

Posted

技术标签:

【中文标题】''~'' 和 ''^'' 如何与 PostgreSQL 中的实际示例一起使用?【英文标题】:How ''~'' and ''^'' actually works with practical examples in PostgreSQL? 【发布时间】:2021-12-30 11:15:00 【问题描述】:

我正在尝试解决很多用户使用包含“~”的语法的情况。 如下:

select 
  business_postal_code as zip, 
  count(distinct case when left(business_address,1) ~ '^[0-9]' then lower(split_part(business_address, ' ', 2)) 
      else lower(split_part(business_address, ' ', 1)) end ) as n_street 
from sf_restaurant_health_violations
where business_postal_code is not null
group by 1
order by 2 desc, 1 asc;

访问案例的链接:https://platform.stratascratch.com/coding/10182-number-of-streets-per-zip-code?python=

但我无法理解这部分代码实际上是如何工作的:... ~ '^ ....

【问题讨论】:

~: Difference between LIKE and ~ in Postgres, ^: Carets in Regular Expressions 这能回答你的问题吗? Regular expression in PostgreSQL LIKE clause 老实说,我看到它们与我的问题有关。但由于他们使用的词汇,我无法理解。出于这个原因,我征求了实际的例子,因为很多时候用这个词汇和更有经验的人使用的另一种表达方式很难理解。 快速入门。 ~ 是正则表达式匹配运算符,如果左侧文本操作数与右侧正则表达式操作数匹配,则返回 true。 '12345' ~ '23' 返回真,'12345' ~ '24' - 假。 ^ 意味着两个不同的东西。如果它位于正则表达式的开头,则表示“字符串的开头”。所以'12345' ~ '^23' 是假的,因为字符串不是以'23' 开头的。如果 ^ 是范围的第一个字符 [^1-9A-F],它会否定范围,即所有 除了十六进制字母和数字。 【参考方案1】:

让我们将您问题中的查询简化为您要询问的组成部分。一旦我们看到它们是如何单独工作的,也许整个查询会更有意义。


首先,~(波浪号)是POSIX,区分大小写的regular expression operator。链接的 PostgreSQL 文档提供了它及其兄弟运算符的简要描述和使用示例:

Operator Description Example
~ Matches regular expression, case sensitive 'thomas' ~ '.*thomas.*'
~* Matches regular expression, case insensitive 'thomas' ~* '.*Thomas.*'
!~ Does not match regular expression, case sensitive 'thomas' !~ '.*Thomas.*'
!~* Does not match regular expression, case insensitive 'thomas' !~* '.*vadim.*'

我们可以看到每个运算符都有两个操作数:左边是一个常量字符串,右边是一个模式。如果左边的字符串与右边的模式匹配,则语句为true,否则为false

在您询问的运算符的给定示例中,'thomas' 与标准正则表达式规则的模式 '.*thomas.*' 匹配。 '.*' 前后缀表示“匹配 any character (except newline) 任意次数 (zero or more)”。整个模式的意思是,“匹配任何字符任意多次,然后匹配文字字符串 'thomas',然后任意匹配任意字符”。一个这样的匹配是'john thomas jones',其中'john ' 匹配第一个'.*'' jones' 匹配第二个'.*'

我不认为这是一个很好的例子,因为它在功能上等同于'thomas' LIKE '%thomas%',后者可能运行得更快,among other benefits like being a SQL-standard operator。

一个更好的例子是问题中使用'^[0-9]' 模式的查询。暂时将^ 放在一边,这个模式的意思是“匹配0-9 (0, 1, 2, ..., 8, 9) 中的任何字符”,如果你使用LIKE 运营商:field LIKE '^0' OR field LIKE '^1' OR field LIKE '^2' ....


^ 运算符不是 PostgreSQL 特定的。相反,它是正则表达式中的特殊字符,具有两种含义之一(aside from its use as a literal character; more about that in this answer):

    匹配应从行/字符串的开头开始。

例如,字符串“Hello, World!” 包含与模式'World' 的匹配项,因为其中出现了单词“World”,但不会包含与模式'^World' 的匹配项,因为单词“World”不在开头字符串。

字符串“你好,世界!”将包含以下两种模式的匹配项:'Hello''^Hello',因为单词“Hello”位于字符串的开头。

    在进行匹配时,给定的字符集应该被否定。

例如,模式[^0-9] 的意思是“匹配任何不在0-9 范围内的字符”。所以'a' 会匹配,'&' 会匹配,'G' 会匹配,但'7' 不会匹配,因为它在被排除的字符集中。

您问题中的查询使用两种含义中的第一种。模式'^[0-9]' 表示“匹配从字符串开头开始的0-9 范围内的任何字符”。所以'0123' 会匹配,因为字符串以“0”开头,但'a5' 不会匹配,因为字符串以“a”开头,这不是要匹配的字符集。


然后,回到您问题中的查询。相关部分内容如下:

1    count(distinct 
2        case 
3            when left(business_address, 1) ~ '^[0-9]' 
4                then lower(split_part(business_address, ' ', 2)) 
5            else lower(split_part(business_address, ' ', 1)) 
6        end 
7    ) as n_street 

第 3 行包含一个正则表达式匹配,它将确定我们是否应该在整个 CASE 语句中使用这种情况。如果字符串与模式匹配,则表达式将为true,我们将使用这种情况。如果字符串与模式不匹配,则表达式将为false,我们将尝试下一个案例。

我们与模式匹配的字符串是left(business_address, 1)LEFT function 从字符串中获取第一个 n 字符。由于此处n 为“1”,因此返回字段business_address 的第一个字符。

我们试图匹配这个字符串的模式是'^[0-9]',我们已经说过,“匹配从字符串开头开始的 0-9 范围内的任何字符”。从技术上讲,我们在这里不需要^ 正则表达式运算符,因为LEFT(..., 1) 最多会返回一个字符(它将始终是结果字符串中的第一个字符)。

例如,如果business_address 是“123 Jones Street, Anytown, USA”,那么 LEFT(business_address, 1) 将返回匹配模式的“1”(因此表达式将为 true,我们将使用第一种情况)。

如果 business_address 是“Jones Plaza, Suite 123, Anytown, USA”,则 LEFT(business_address, 1) 将返回与模式不匹配的“J”(因为第一个字符是“J”,而不是在 0-9 范围内)。我们的表达式是false,我们将继续下一个案例。

【讨论】:

以上是关于'~' 和 '^' 如何与 PostgreSQL 中的实际示例一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

PostgreSQL - 如何在单个查询中获取列的最小值和最大值以及与它们关联的行?

如何使用与PostgreSQL和Python的模式匹配与多个百分比(%)符号?

将 PDO 与 PostgreSQL 一起使用时如何忽略问号作为占位符

Heroku 和 Django 组合中的 Postgresql 更改与共享数据库

如何在 Windows 10 中安装 PostgreSQL 和连接设置

如何使 php 与 postgresql 一起工作?