Sql 递归查询中的唯一性保证
我有一个非常基本的父子/树层次结构和一个递归查询,它还添加了Sql 递归查询中的唯一性保证,sql,postgresql,Sql,Postgresql,我有一个非常基本的父子/树层次结构和一个递归查询,它还添加了深度,几乎完美地加载了所有内容。当我尝试加载多个节点时,其中一个节点是另一个节点的子节点,我会得到重复的行(因为depth会更新,并且行不再相同) 我确实阅读了,我没有使用UNION ALL,我确实尝试了文档中的not cycle技巧,我知道数据类型,但我不能使用它。这是另外一回事,考虑下面的树: 5 ├─9 │ └─15 ├─10 │ └─16 └─11 └─17 以及查询: WITH RECURSIVE "CTE" AS (
深度
,几乎完美地加载了所有内容。当我尝试加载多个节点时,其中一个节点是另一个节点的子节点,我会得到重复的行(因为depth
会更新,并且行不再相同)
我确实阅读了,我没有使用UNION ALL
,我确实尝试了文档中的not cycle
技巧,我知道数据类型,但我不能使用它。这是另外一回事,考虑下面的树:
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
使用运行相同的查询,其中“id”=5
会产生以下结果(请注意,深度有多大差异,因为选择是从根开始的):
解决方案是将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";
您想要的输出到底是什么?以及(5、9、15)中的“id”有什么特别之处?它们只构成了树中的一条可能路径。您的查询很简单,正如您所问的:它返回一个具有三个根节点(5、9、15)的林。但从层次结构来看,这些节点不在同一级别上(这就是为什么存在重复节点:林中的每棵树对应一个)。如果您只想查询示例树,您只需要
其中“id”=5
。@a_horse_与_no_name道歉,添加了预期的输出。@wildplasser没有什么特别之处,它可以是5、10或任何其他包含来自同一层次结构分支的元素的节点集。哇,这太疯狂了!我没有想到要在cte之外消除复制品。谢谢是的,有一个强烈的倾向,修剪在早期阶段。(在您必须中止它们之前避免它们…)我昨天查看了过滤,没有找到任何解决方法。我刚刚在(“id”)上选中了DISTINCT,它在没有子查询的情况下完成任务,按“id”排序,“depth”DESC
使它也按最大深度排序。我等着看是否有人有其他想法。谢谢你,你给了我一个完全正确的主意!最后,我选择了DISTINCT ON
。谢谢你的解决方案!
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";
-- 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
;
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
;