8

我创建了一个简单的例子来说明在PostgreSQL中使用递归查询的传递闭包。用于传递闭包的递归查询

但是,我的递归查询是关闭的。我对语法还不熟悉,所以这个请求对我来说可能完全没有意义,为此我提前表示歉意。如果运行查询,您将看到节点1在路径结果中重复自己。有人可以帮我弄清楚如何调整SQL?

/*   1 
     / \ 
      2  3 
     /\ /
     4 5 6 
    /
     7 
    /\ 
    8 9 
*/ 

create table account(
acct_id INT, 
parent_id INT REFERENCES account(acct_id), 
acct_name VARCHAR(100), 
PRIMARY KEY(acct_id) 
); 

insert into account (acct_id, parent_id, acct_name) values (1,1,'account 1'); 
insert into account (acct_id, parent_id, acct_name) values (2,1,'account 2'); 
insert into account (acct_id, parent_id, acct_name) values (3,1,'account 3'); 
insert into account (acct_id, parent_id, acct_name) values (4,2,'account 4'); 
insert into account (acct_id, parent_id, acct_name) values (5,2,'account 5'); 
insert into account (acct_id, parent_id, acct_name) values (6,3,'account 6'); 
insert into account (acct_id, parent_id, acct_name) values (7,4,'account 7'); 
insert into account (acct_id, parent_id, acct_name) values (8,7,'account 8'); 
insert into account (acct_id, parent_id, acct_name) values (9,7,'account 9'); 

WITH RECURSIVE search_graph(acct_id, parent_id, depth, path, cycle) AS (
     SELECT g.acct_id, g.parent_id, 1, 
      ARRAY[g.acct_id], 
      false 
     FROM account g 
     UNION ALL 
     SELECT g.acct_id, g.parent_id, sg.depth + 1, 
      path || g.acct_id, 
      g.acct_id = ANY(path) 
     FROM account g, search_graph sg 
     WHERE g.acct_id = sg.parent_id AND NOT cycle 
) 
SELECT path[1] as Child,parent_id as Parent,path || parent_id as path FROM search_graph 
ORDER BY path[1],depth; 
+0

您的测试设置稳定且有用,但请解释您的结果应该看起来像什么样子?抛弃关键词“传递闭包”是不足以解释的。 –

回答

4

可以在几个地方简化(假设acct_idparent_idNOT NULL):

WITH RECURSIVE search_graph AS (
    SELECT parent_id, ARRAY[acct_id] AS path 
    FROM account 

    UNION ALL 
    SELECT g.parent_id, sg.path || g.acct_id 
    FROM search_graph sg 
    JOIN account g ON g.acct_id = sg.parent_id 
    WHERE g.acct_id <> ALL(sg.path) 
    ) 
SELECT path[1] AS child 
    , path[array_upper(path,1)] AS parent 
    , path 
FROM search_graph 
ORDER BY path; 
  • acct_iddepthcycle是在查询中只有噪音。
  • WHERE条件必须先退出递归一步,之前顶部节点的重复条目在结果中。这是你的原创作品中的一个“一个接一个”。

其余的格式。

如果您知道图表中的唯一可能的圈是一个自我参照,我们可以有更便宜:

WITH RECURSIVE search_graph AS (
    SELECT parent_id, ARRAY[acct_id] AS path, acct_id <> parent_id AS keep_going 
    FROM account 

    UNION ALL 
    SELECT g.parent_id, sg.path || g.acct_id, g.acct_id <> g.parent_id 
    FROM search_graph sg 
    JOIN account g ON g.acct_id = sg.parent_id 
    WHERE sg.keep_going 
) 
SELECT path[1] AS child 
    , path[array_upper(path,1)] AS parent 
    , path 
FROM search_graph 
ORDER BY path; 

SQL Fiddle.

注意就不会有问题(至少到pg v9.4)的数据类型与修饰符(如varchar(5)),因为数组连接失去了修饰符,但rCTE坚持精确匹配类型:

+0

非常感谢你的深思熟虑的回应!这两个查询(甚至后者)都按照我的意图完成。 – Dowwie

0

您已将帐户1设置为其自己的父级。如果您将该帐户的父级设置为null,则可以避免将该帐户同时作为开始和结束节点(您的逻辑设置方式包括一个周期,但不会添加到该周期,这似乎是合理的)。将最终的“路径”列更改为类似case when parent_id is not null then path || parent_id else path end这样的列也会更好一些,以避免在最后出现null。

+0

您是否运行我的样本并确认?当我将帐户1的插入更改为空的父级时,它不会改进结果。 – Dowwie