如何在正则表达式上连接表

时间:2011-12-30 11:40:12

标签: sql regex postgresql join pattern-matching

假设我有两个消息表msg和移动网络代码mnc。 他们没有任何关系。但我想加入他们

SELECT msg.message,
    msg.src_addr,
    msg.dst_addr,
    mnc.name,
FROM "msg"
JOIN "mnc"
ON array_to_string(regexp_matches(msg.src_addr || '+' || msg.dst_addr, '38(...)'), '') = mnc.code

但查询失败并显示错误:

psql:marketing.sql:28: ERROR:  argument of JOIN/ON must not return a set
LINE 12: ON array_to_string(regexp_matches(msg.src_addr || '+' || msg...

有没有办法进行此类加入?还是我走错路?

3 个答案:

答案 0 :(得分:14)

正如@Milen已经提到的regexp_matches()可能是错误的功能。你想要一个简单的regular expression match (~)。实际上,LIKE operator (~~)更快

大概是LIKE

最快
SELECT msg.message
      ,msg.src_addr
      ,msg.dst_addr
      ,mnc.name
FROM   mnc
JOIN   msg ON msg.src_addr ~~ ('%38' || mnc.code || '%')
           OR msg.dst_addr ~~ ('%38' || mnc.code || '%')
WHERE  length(mnc.code) = 3

此外,您只需要{3}个字符的mnc.code


使用regexp

可以用正则表达式编写相同内容,但它肯定会慢一些。这是一个接近原文的工作示例:

SELECT msg.message
      ,msg.src_addr
      ,msg.dst_addr
      ,mnc.name
FROM   mnc
JOIN   msg ON (msg.src_addr || '+' || msg.dst_addr) ~ (38 || mnc.code)
           AND length(mnc.code) = 3

这还需要msg.src_addrmsg.dst_addrNOT NULL

第二个查询演示了附加检查length(mnc.code) = 3如何进入JOIN条件或WHERE子句。这里效果相同。


使用regexp_matches()

可以使用regexp_matches()

来完成这项工作
SELECT msg.message
      ,msg.src_addr
      ,msg.dst_addr
      ,mnc.name
FROM   mnc
JOIN   msg ON EXISTS (
    SELECT * 
    FROM   regexp_matches(msg.src_addr ||'+'|| msg.dst_addr, '38(...)', 'g') x(y)
    WHERE  y[1] = mnc.code
    )

但相比之下它会很慢 - 或者我认为。

<强>解释
你的regexp_matches()表达式只返回第一个匹配的所有捕获的子串的数组。由于您只捕获一个子字符串(模式中的一对括号),因此您将只使用一个元素来获得数组。

您使用其他“全局”开关 'g' 所有匹配 - 但是在多行中。所以你需要一个子选择来测试它们(或聚合)。把它放在EXISTS - 半连接中,你得到你想要的东西。

也许您可以通过所有三个效果测试报告回来? 请使用EXPLAIN ANALYZE

答案 1 :(得分:0)

您的直接问题是regexp_matches可能会返回一行或多行。

答案 2 :(得分:0)

尝试使用“substring”,它会在给定正则表达式模式的情况下提取子字符串。

SELECT msg.message,
    msg.src_addr,
    msg.dst_addr,
    mnc.name
FROM "msg"
JOIN "mnc"
ON substring(msg.src_addr || '+' || msg.dst_addr from '38(...)') = mnc.code