我有一个非常基本的父子/树层次结构和一个递归查询,它还添加了depth
并且几乎完全加载了所有内容。当我尝试加载多个节点时,其中一个节点是另一个节点的子节点,我得到重复的行(因为depth
得到更新,行不再相同)。
我确实阅读了documentation,我没有使用UNION ALL
,我确实尝试了文档中的NOT cycle
技巧,我知道ltree数据类型,但是我不能用它。这是另外一点,请考虑以下树:
5
├─9
│ └─15
├─10
│ └─16
└─11
└─17
查询:
WITH RECURSIVE "CTE" AS
(
SELECT "id", 0 AS "depth"
FROM "Node" WHERE "id" IN (5, 9, 15)
UNION
SELECT "Node"."id", "CTE"."depth" + 1
FROM "CTE" JOIN "Node" ON "Node"."parentId" = "CTE"."id"
)
SELECT *
FROM "CTE"
ORDER BY "id";
结果是:
id depth
5 0
9 0
9 1
10 1
11 1
15 0
15 1
15 2
16 2
17 2
取代期望的结果:
id depth
5 0
9 0
10 1
11 1
15 0
16 2
17 2
使用WHERE "id" = 5
运行相同的查询会产生这种情况(请注意,深度有何不同,因为选择是从根目录开始的):
id depth
5 0
9 1
10 1
11 1
15 2
16 2
17 2
解决方法是将join修改为:
FROM "CTE" JOIN "Node" ON
"Node"."parentId" = "CTE"."id" AND
"Node"."id" NOT IN (SELECT "id" FROM "CTE")
但Postgres不允许从子查询引用“CTE”。我想知道是否有正确解决这个问题的方法?
顺便说一下,我确实提出了一个有效的解决方案,我在几个不同的场景中尝试过,但我并不是100%肯定它会在所有情况下都有效。它基本上消除了最初选择的值,确保迭代不会“输入”它们。我是否正确地使用它/这种方法有任何陷阱吗?
WITH RECURSIVE "CTE" AS
(
SELECT "id", 0 AS "depth"
FROM "Node" WHERE "id" IN (5, 9, 15)
UNION
SELECT "Node"."id", "CTE"."depth" + 1
FROM "CTE" JOIN "Node" ON
"Node"."parentId" = "CTE"."id"
AND NOT IN (5, 9, 15)
)
SELECT *
FROM "CTE"
ORDER BY "id";
答案 0 :(得分:1)
-- Data
CREATE TABLE node
( id integer NOT NULL PRIMARY KEY
, parentid integer REFERENCES node(id)
);
INSERT INTO node(id,parentid) VALUES
(5, NULL)
, (9,5), (10,5), (11,5)
, (15,9), (16,10), (17,11)
;
-- query
WITH RECURSIVE tree AS (
SELECT id, 0 AS depth
FROM node WHERE id IN (5, 9, 15)
UNION
SELECT node.id, tree.depth + 1
FROM tree JOIN node ON node.parentid = tree.id
)
SELECT *
FROM tree tr
WHERE NOT EXISTS ( -- trivial way to suppress duplicates with longer path
SELECT *
FROM tree nx
WHERE nx.id = tr.id
AND nx.depth < tr.depth
)
ORDER BY id
;
更新:这看起来成本更低。这对于给定的数据是正确的(但在一般情况下不是IIUC):
WITH RECURSIVE tree AS (
SELECT id, 0 AS depth
FROM node WHERE id IN (5, 9, 15)
UNION
SELECT node.id, tree.depth + 1
FROM tree JOIN node ON node.parentid = tree.id
WHERE node.id NOT IN (5, 9, 15)
)
SELECT *
FROM tree tr
ORDER BY id
;