按照连接:递归查询

Follow the connections : recursive query

我有两列数字。 目标从数字 f.e 开始。 55,提取所有 'connected' 个数字 (从这些数字中的任何一个开始,都应该产生相同的结果)

 A  B
-----
56  55
55  56
69  55
35  55
47  55
60  55
22  55
26  47
 2  35
..... more data

在这种情况下,此处显示的所有数字:55,56,35,69,60,22,47,2,26

我使用以下查询:

with recursive merge as (
   select A from T where A = 55 or B = 55
   union 
   select B 
    from T cm join merge m 
       on cm.A = m.A or cm.B = m.A
)
select * from merge

但我只找回那些:55,56,35,69,60,22,47

我认为使用它会有效:

with recursive merge as (
   select A from T where A = 55 or B = 55
   union 

  (
   select B 
    from T cm join merge m 
       on cm.A = m.A
    union
   select A 
    from T cm join merge m 
       on cm.B = m.A
  )
)
select * from merge

但 Postgres 不允许在递归查询中多次使用“合并”!!?

我的目标是从数字开始,找到“链”中所有连接的数字,即

  55 => 56,35,69,60,22,47 => 2,26 ....

因为 :

  47 => 26
  35 => 2

你可以这样做:

with recursive
n (v) as (
  select case when t.a = 55 then t.b else t.a end
  from t
  where t.a = 55 or t.b = 55
 union
  select case when t.a = n.v then t.b else t.a end
  from n
  join t on t.a = n.v or t.b = n.v
)
select * from n

结果:

 v
--
56
69
35
47
60
22
55
 2
26

参见 DB Fiddle 中的 运行 示例。

或者...如果您更喜欢将参数放在一个地方:

with recursive
params as (select 55 as x), -- parameter only once
n (v) as (
  select case when t.a = p.x then t.b else t.a end
  from t
  cross join params p 
  where t.a = p.x or t.b = p.x
 union
  select case when t.a = n.v then t.b else t.a end
  from n
  join t on t.a = n.v or t.b = n.v
)
select * from n

最后 UNION 编辑两个 CTE 怎么样?

WITH RECURSIVE
m1
AS
(
SELECT t.a
       FROM t
       WHERE a = 55
              OR b = 55
UNION
SELECT t.b 
       FROM t
            INNER JOIN m1
                       ON t.a = m1.a
),
m2
AS
(
SELECT t.a
       FROM t
       WHERE a = 55
              OR b = 55
UNION
SELECT t.a
       FROM t
            INNER JOIN m1
                       ON t.b = m1.a
)
SELECT m1.a
       FROM m1
UNION
SELECT m2.a
       FROM m2;

db<>fiddle

这是一个无向图。因此,一种解决方案是创建所有边,然后跟随它们。

为避免无限循环,保留已访问节点的列表:

with recursive pairs as (
      select a, b
      from t
      union  -- on purpose to remove duplicates
      select b, a
      from t
     ),
     cte as (
      select p.a, p.b, array[p.a, p.b] as els
      from pairs p
      where a = 55
      union all
      select p.a, p.b, els || p.b
      from cte join
           pairs p
           on cte.b = p.a
      where not (p.b = any (cte.els))
)
select b
from cte;

Here 是一个 db<>fiddle.