英文:
How to avoid prohibited outer join in recursive CTE?
问题
我有一个表示对象之间无环依赖关系的表:
CREATE TEMPORARY TABLE dependencies
(
obj_id bigint,
depended_upon bigint NULL
);
INSERT INTO dependencies
VALUES
(1, NULL), -- 1不依赖于任何其他对象
(2, 1), -- 1依赖于2
(3, 4), -- 4依赖于3
(3, 2), -- ...2也是如此
(4, NULL); -- 4也不依赖于任何其他对象
我想确定从叶子节点开始修剪对象以确保不违反依赖关系的顺序。对于这个示例,结果如下所示:
对象ID | 修剪步骤 |
---|---|
4 | 0 |
1 | 0 |
2 | 1 |
3 | 2 |
每个对象在没有其他对象依赖于它的第一步中被修剪。
我尝试使用递归CTE来实现这个目标:
WITH RECURSIVE deletion_order(obj_id, step) AS (
SELECT obj_id, 0
FROM dependencies
GROUP BY obj_id
HAVING COUNT(depended_upon) = 0
UNION ALL
SELECT dependencies.obj_id, step + 1
FROM dependencies
LEFT JOIN deletion_order ON dependencies.depended_upon = deletion_order.obj_id
WHERE deletion_order.obj_id IS NULL
)
SELECT *
FROM deletion_order
ORDER BY step;
但Postgres报错:
> [42P19] ERROR: recursive reference to query "deletion_order" must not appear within an outer join
如何在不使用外连接的情况下实现这一目标?请注意,dependencies
的格式是灵活的,如果需要改变以适应解决方案,也是可以的。
英文:
I have a table which represents acyclic dependencies between objects:
CREATE TEMPORARY TABLE dependencies
(
obj_id bigint,
depended_upon bigint NULL
);
INSERT INTO dependencies
VALUES
(1, NULL), -- 1 is not depended upon by any other object
(2, 1), -- 1 depends on 2
(3, 4), -- 4 depends on 3
(3, 2), -- ... and so does 2
(4, NULL); -- 4 also is not depended upon by any other object
I'd like to determine the order in which I would have to prune objects so that no dependencies are violated, starting from the leaves. For this example, that result would look like this:
Object ID | Prune step |
---|---|
4 | 0 |
1 | 0 |
2 | 1 |
3 | 2 |
Each object is pruned in the first step in which no other object depends on it.
I've tried to do this with a recursive CTE:
WITH RECURSIVE deletion_order(obj_id, step) AS (
SELECT obj_id, 0
FROM dependencies
GROUP BY obj_id
HAVING COUNT(depended_upon) = 0
UNION ALL
SELECT dependencies.obj_id, step + 1
FROM dependencies
LEFT JOIN deletion_order ON dependencies.depended_upon = deletion_order.obj_id
WHERE deletion_order.obj_id IS NULL
)
SELECT *
FROM deletion_order
ORDER BY step;
But Postgres complains:
> [42P19] ERROR: recursive reference to query "deletion_order" must not appear within an outer join
How can I accomplish this without an outer join? Note that the format of dependencies
is flexible, if it needs to change to accommodate the solution.
答案1
得分: 1
如果我理解你尝试实现的内容:你已经解决了大多数递归查询的问题,但在最简单的地方出现了问题:
- 你的CTE的初始化可以更简单,使用
WHERE depended_upon IS NULL
。 - 我不理解为什么你在CTE的递归部分中加入了
WHERE deletion_order.obj_id IS NULL
。 - 连接应该是内连接,我不明白为什么你认为需要外连接(我猜这与前一个问题有关,但这没有帮助我理解)。
最终的查询:
WITH RECURSIVE deletion_order(obj_id, step) AS (
SELECT obj_id, 0
FROM dependencies
WHERE depended_upon IS NULL
UNION ALL
SELECT dependencies.obj_id, step + 1
FROM dependencies
JOIN deletion_order ON dependencies.depended_upon = deletion_order.obj_id
)
SELECT obj_id, MAX(step) AS MaxStep /*, MIN(step) AS MinStep(可选,见下面的注释)*/
FROM deletion_order
ORDER BY MaxStep;
所以我不知道如何回答你的问题(如何避免外连接),因为你本来不应该觉得需要在首次使用外连接。
我之前回答了一个关于如何构建递归CTE的问题,也许值得查看。
英文:
If I understood what you are trying to achieve: you have most of the recursive queries figured out but somehow failed where it was the easiest:
- The initialization of your CTE could be easier with
WHERE depended_upon IS NULL
. - I did not understand why you had put
WHERE deletion_order.obj_id IS NULL
in the recursive part of the CTE. - The
JOIN
should be an inner join, I did not understand why you thought you needed an outer join (I guess it is linked with the previous point but that did not help me understand).
Resulting query:
WITH RECURSIVE deletion_order(obj_id, step) AS (
SELECT obj_id, 0
FROM dependencies
WHERE depended_upon IS NULL
UNION ALL
SELECT dependencies.obj_id, step + 1
FROM dependencies
JOIN deletion_order ON dependencies.depended_upon = deletion_order.obj_id
)
SELECT obj_id, MAX(step) AS MaxStep /*, MIN(step) AS MinStep (optional, see comment below)*/
FROM deletion_order
ORDER BY MaxStep;
So I do not know how to answer your question (How to avoid outer join) because you should not have felt the need to use an outer join in the first place.
I have answered a previous question as how to build a recursive CTE here. Maybe it is worth checking.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论