如何对子查询的结果使用正则表达式?
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 好的。发布的所有示例在一个表格中的所有数字都具有相同的长度,我认为这是规则。如果不是这种情况,那么您获得重复项是正确的。以上是关于如何对子查询的结果使用正则表达式?的主要内容,如果未能解决你的问题,请参考以下文章