基于单行匹配选择相关行

时间:2017-11-15 14:28:13

标签: sql postgresql

我在Postgres SQL 9.5上运行了下表:

+---+------------+-------------+
|ID |  trans_id  |   message   |
+---+------------+-------------+
| 1 | 1234567    | abc123-ef   |
| 2 | 1234567    | def234-gh   |
| 3 | 1234567    | ghi567-ij   |
| 4 | 8902345    | ced123-ef   |
| 5 | 8902345    | def234-bz   |
| 6 | 8902345    | ghi567-ij   |
| 7 | 6789012    | abc123-ab   |
| 8 | 6789012    | def234-cd   |
| 9 | 6789012    | ghi567-ef   |
|10 | 4567890    | abc123-ab   |
|11 | 4567890    | gex890-aj   |
|12 | 4567890    | ghi567-ef   |
+---+------------+-------------+

我正在根据trans_id查询查找每个LIKE的行,如下所示:

SELECT * FROM table 
WHERE message LIKE '%def-234%'

这当然只返回三行,这三行与message列中的模式匹配。相反,我正在寻找的是与匹配的消息组中的trans_id匹配的所有行。也就是说,如果单个行与模式匹配,则获取具有该匹配行的trans_id的所有行。

也就是说,结果将是:

+---+------------+-------------+
|ID |  trans_id  |   message   |
+---+------------+-------------+
| 1 | 1234567    | abc123-ef   |
| 2 | 1234567    | def234-gh   |
| 3 | 1234567    | ghi567-ij   |
| 4 | 8902345    | ced123-ef   |
| 5 | 8902345    | def234-bz   |
| 6 | 8902345    | ghi567-ij   |
| 7 | 6789012    | abc123-ab   |
| 8 | 6789012    | def234-cd   |
| 9 | 6789012    | ghi567-ef   |
+---+------------+-------------+

注意第10,11和12行未被SELECT编辑,因为其中没有一行符合%def-234%模式。

我尝试(并且失败)编写子查询以在单个message匹配模式时获取所有相关行:

SELECT sub.*
    FROM (
        SELECT DISTINCT trans_id FROM table WHERE message LIKE '%def-234%'
    ) sub
WHERE table.trans_id = sub.trans_id

我可以使用 两个 查询轻松完成此操作,但是第一个获取匹配trans_id列表的查询要包含在{{1}中}子句会非常大,并且不会是一种非常低效的方法,并且我相信有一种方法可以通过单个查询来完成它。

谢谢!

3 个答案:

答案 0 :(得分:1)

试试这个:

SELECT ID, trans_id, message
FROM (
   SELECT ID, trans_id, message,
          COUNT(*) FILTER (WHERE message LIKE '%def234%') 
                   OVER (PARTITION BY trans_id)  AS pattern_cnt
   FROM mytable) AS t
WHERE pattern_cnt >= 1  

FILTER函数的窗口化版本中使用COUNT子句,我们可以获得与每个trans_id切片中的预定义模式匹配的记录数。外部查询使用此计数来过滤掉不相关的切片。

Demo here

答案 1 :(得分:1)

这将完成我认为的工作:

WITH sub AS (
    SELECT trans_id 
      FROM table 
     WHERE message LIKE '%def-234%'
)
SELECT *
  FROM table JOIN sub USING (trans_id);

希望得到这个帮助。

答案 2 :(得分:1)

你可以这样做。

WITH trans
AS
(SELECT DISTINCT trans_id 
   FROM t1 
  WHERE message LIKE '%def234%')
SELECT t1.*
 FROM t1,
      trans
WHERE t1.trans_id = trans.trans_id;

我认为这会表现得更好。如果您有足够的数据,您可以对Sub查询和CTE进行解释并比较输出。