Postgresql中的无限递归函数异常

时间:2017-03-24 08:48:57

标签: postgresql postgresql-9.4

我有一个表答案和多对多表链接(答案n-n答案)

链接有2列:from_id和to_id引用answer_id。 我想通过answer_id(Link中的from_id)获得所有答案的后代。

我写的功能如下:

CREATE OR REPLACE FUNCTION getAllChild(_answer_id BIGINT)
RETURNS SETOF BIGINT AS $$
DECLARE r link;
BEGIN
  FOR r IN 
    SELECT * FROM link
       WHERE from_id = _answer_id
  LOOP
    RETURN NEXT r.to_id;
    RETURN QUERY SELECT * FROM getAllChild(r.to_id);
  END LOOP;
  RETURN;
END;
$$ LANGUAGE plpgsql STRICT;

SELECT * FROM getAllChild(1);

如果to_id没有与from_id重复,那么结果很好,否则我会得到递归的无穷大。

我的问题是如何让循环跳过现有的to_id来调用RETURN QUERY中的getAllChild()

1 个答案:

答案 0 :(得分:1)

我建议您使用递归CTE执行此操作,但您可以在函数中使用相同的方法。

您可以使用数组来跟踪您所处理的所有from_id,然后在下一次运行中忽略结果中已有的from_id的所有记录。在下面的代码中,我使用path数组来跟踪已经看到的所有from_id。

with recursive t as
(
  select l.from_id,l.to_id, ARRAY[l.from_id] as path, 1 as depth 
  from link l where from_id = 2
union all 
  select l.from_id,l.to_id, array_append(t.path,l.from_id), t.depth+1 
  from link l 
  inner join t on l.from_id = t.to_id
  where not (l.from_id = ANY (t.path))  -- ignore records already processed
)
select * from t;

小提琴:http://sqlfiddle.com/#!15/024e80/1

更新:作为一项功能

CREATE OR REPLACE FUNCTION getAllChild(_answer_id BIGINT)
RETURNS SETOF BIGINT AS $$
BEGIN
  return query 
    with recursive t as
    (
    select l.from_id,l.to_id, ARRAY[l.from_id] as path, 1 as depth from link l where from_id = _answer_id
    union all 
    select l.from_id,l.to_id, array_append(t.path,l.from_id), t.depth+1 from link l 
    inner join t on l.from_id = t.to_id
    where not (l.from_id = ANY (t.path))
    )
    select to_id from t;
END;
$$ LANGUAGE plpgsql STRICT;

数组文档:https://www.postgresql.org/docs/current/static/arrays.html

CTE:https://www.postgresql.org/docs/current/static/queries-with.html