Postgresql中的无限递归函数异常
Infinity recursive function exception in Postgresql
我有一个 table 答案和多对多 table Link(答案 n-n 答案)
Link 有 2 列:from_id 和 to_id 对 answer_id 的引用。
我想通过 answer_id ( from_id in Link )获得答案的所有后代。
我写的函数如下:
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()
我建议您使用递归 CTE 执行此操作,不过您可以在函数中使用相同的方法。
您可以使用数组来跟踪您处理过的所有 from_id,然后在接下来的 运行 中忽略 [=28= 的任何记录] 已经在结果中。在下面的代码中,我使用 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;
Fiddle 在: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
我有一个 table 答案和多对多 table Link(答案 n-n 答案)
Link 有 2 列:from_id 和 to_id 对 answer_id 的引用。 我想通过 answer_id ( from_id in Link )获得答案的所有后代。
我写的函数如下:
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()我建议您使用递归 CTE 执行此操作,不过您可以在函数中使用相同的方法。
您可以使用数组来跟踪您处理过的所有 from_id,然后在接下来的 运行 中忽略 [=28= 的任何记录] 已经在结果中。在下面的代码中,我使用 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;
Fiddle 在: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