如何对子查询的结果使用正则表达式?

Posted

技术标签:

【中文标题】如何对子查询的结果使用正则表达式?【英文标题】:How to use regexp on the results of a sub query? 【发布时间】:2016-04-30 10:51:34 【问题描述】:

我有两张桌子。

用户 其中有 id电话号码


id phone_no

1 ---- 9912678

2 ---- 9912323

3 ---- 9912366


准入表,有id 电话号码

id phone_no

6 --- 991267823

7 --- 991236621

8 --- 435443455

9 --- 243344333

我想查找与 users 表和 update 具有相同模式的 Admission's 表的所有电话号码 > 在用户表中。

所以我正在尝试这个

select phone_no  from admission where phone_no REGEXP (SELECT phone_no
FROM  `users` AS user
WHERE user.phone_no REGEXP  '^(99)+[0-9]8')

但我收到此错误子查询返回超过 1 行

寻求帮助。

【问题讨论】:

“具有相同的模式”究竟是什么意思?从您的示例中猜测,它实际上是“开始于”吗? 用户表和准入表都有相同的电话号码,以 99 开头,但用户表缺少尾随 2 位。首先,我想从录取表 '^[99]+[0-9]8' 中过滤所有这些号码,然后将用户的电话号码 9912678 与录取的电话号码 991267823 匹配 如果我查询以这种方式格式化的用户表的数据 '9912678|9912323|9912366' 查询执行得很好,但我不知道该怎么做。谢谢 模式“[99]+”应该是什么意思?它等于'[9]+',即等于'9+'。也许你的意思是“(99)+”或者只是“99”。 @PaulSpiegel 是的,你是对的。应该是'(99)+' 【参考方案1】:

尝试以下查询之一:

SELECT a.phone_no
FROM admission a
JOIN users u on a.phone_no LIKE concat(u.phone_no, '__')
WHERE u.phone_no REGEXP  '^(99)+[0-9]+$'

SELECT a.phone_no
FROM admission a
JOIN users u on a.phone_no REGEXP concat('^', u.phone_no, '[0-9]2$')
WHERE u.phone_no REGEXP  '^(99)+[0-9]+$'

如果“尾数”的个数不固定,也可以使用:

LIKE concat(u.phone_no, '%')

REGEXP concat('^', u.phone_no, '[0-9]*$')

但在这种情况下,如果 users.phone_no 可能是另一个 users.phone_no 的子序列(例如 99123 和 991234),您可能需要使用 SELECT DISTICT a.phone_no

更新

在用 10K 行的 users 表和 100K 行的 admission 表运行一些测试后,我得到了以下查询:

SELECT a.phone_no
FROM admission a
JOIN users u 
    ON  a.phone_no >= u.phone_no
    AND a.phone_no < CONCAT(u.phone_no, 'z')
    AND a.phone_no LIKE CONCAT(u.phone_no, '%')
    AND a.phone_no REGEXP CONCAT('^', u.phone_no, '[0-9]*$')
WHERE   u.phone_no LIKE  '99%'
    AND u.phone_no REGEXP  '^(99)+[0-9]*$'
UNION SELECT 0 FROM (SELECT 0) dummy WHERE 0

fiddle

这样您可以使用REGEXP 并且仍然具有出色的性能。此查询在我的测试用例中几乎立即执行。

从逻辑上讲,您只需要 REGEXP 条件。但在更大的表上,查询可能会超时。使用 LIKE 条件将在 REGEXP 检查之前过滤结果集。但即使使用 LIKE 查询也不能很好地执行。由于某种原因,mysql 不对连接使用范围检查。所以我添加了一个明确的范围检查:

    ON  a.phone_no >= u.phone_no
    AND a.phone_no < CONCAT(u.phone_no, 'z')

通过此检查,您可以从 JOIN 部分中删除 LIKE 条件。

UNION 部分是 DISTICT 的替代品。 MySQL 似乎将 DISTINCT 转换为 GROUP BY 语句,该语句表现不佳。使用带有空结果集的 UNION 我强制 MySQL 在 SELECT 之后删除重复项。如果您使用固定数量的尾随数字,则可以删除该行。

您可以根据需要调整 REGEXP 模式:

...
    AND a.phone_no REGEXP CONCAT('^', u.phone_no, '[0-9]2$')
...
    AND u.phone_no REGEXP  '^(99)+[0-9]8$'
...

如果您只需要 REGEXP 来检查 phone_no 的长度,您还可以使用带有 '_' 占位符的 LIKE 条件。

    AND a.phone_no LIKE CONCAT(u.phone_no, '__')
...
    AND u.phone_no LIKE '99________$'

或将 LIKE 条件与 STR_LENGTH 检查结合起来。

【讨论】:

加入LIKE而不使用DISTINCT会返回重复,最好使用EXISTS而不是加入表格。 谢谢@maraca。然而,DISTINCT 只有在“尾随数字”的数量不固定时才需要。 @PaulSpiegel 您的第一个查询返回空结果,第二个查询返回更多值然后准入表有,第三个工作正常。谢谢 我刚刚更新了我的问题,请您帮忙更新用户表上的值吗? @shuvrow,如果您想使用准入表中找到的匹配项更新用户 phone_no - 那么需要更多信息。但是,您总是可以在 UPDATE 语句中加入带有子查询的表。也许这个对你有帮助:***.com/questions/11588710/…【参考方案2】:

我认为这是你想要的,我做了一些改进 (SQLfiddle):

select * from admission a where exists (
  select * from (
     select substr(phone_no, 1, 7) pn from users where phone_no REGEXP '^99[0-9]5'
  ) o where a.phone_no like concat(o.pn, '%')
)

我必须修改正则表达式才能获得任何匹配项。如果长度是固定的,则可以使用like 轻松完成第二次检查。我们在user 表中查看是否有exists 任何phone_no 与我们当前正在查看的录取编号标准匹配。

【讨论】:

【参考方案3】:

不要介意正则表达式。使用like 进行简单连接

select distinct a.phone_no
from user u
join admission a on a.phone_no like concat(u.phone_no, '%')
where u.phone_no like '99%'

仅当admission 表和/或user 表中存在重复数字时才需要distinct 关键字。否则可以省略。

【讨论】:

对不起,但查询是错误的,并且正是他所抱怨的问题:电话号码将成倍增加(除非你做一个不同的)。 @maraca 你不正确。 OP 得到的错误与从结果中返回重复行无关;这是由于 OP 的 subquery 在查询中用作单值结果时返回多行。在 OP 的查询中使用 distinct 不会使 OP 的错误消失。只有在原始数据中有重复项时才需要在我的查询中使用distinct(我已更新我的答案以解释这一点)。 @maraca 再次,我不同意你所说的一切。需要明确的是,如果 2 个准入号码都匹配(即以)相同的用户号码,则根据 OP 的要求,它们都应该出现在输出中。正如我之前所说,只有在任一表中存在实际重复时,才会出现输出重复。此外,like 是完全合适的,因为语义是“开始于”,这是最简单、最好和最清晰的表达为like 'prefix%'。使用exists 的性能会很差,因为它必须为每一行执行一次子查询,而like 只使用一次。 好吧,我认为它不清楚,所以让我举个例子,用户表 9923、992 和另一个表 99234、99235...没有重复,仍然返回两个数字两次...我真的明白了在我看来,当您尝试在 where 子句中过滤它所属的准入表时,没有理由加入,否则会使事情变得不必要地复杂。 @maraca 好的。发布的所有示例在一个表格中的所有数字都具有相同的长度,我认为这是规则。如果不是这种情况,那么您获得重复项是正确的。

以上是关于如何对子查询的结果使用正则表达式?的主要内容,如果未能解决你的问题,请参考以下文章

数据库笔记-SQL过滤(LIKE&正则表达式)

oracle10g中,如何查询正则表达式匹配指定字符串的匹配个数?

SQL数据库查询使用正则表达式如何查询中文?

如何使用正则表达式选择分组

如何从 Redshift 的正则表达式组中选择第一个结果?

jmeter正则表达式提取数据库查询多个字段的结果应用到下一个请求中?