我有一个表stories
和一个表blockings
,其中有列story_id
(引用一个故事)和一个blocked_story_id
(也引用一个故事,被story_id
屏蔽)
我正试图构造一个查询,以根据其阻止者的优先顺序返回所有故事-因此,阻止者首先要遍历树。
一个故事可以被许多故事遮挡,并且本身可以成为许多故事的遮挡物。
我一直在阅读和重新阅读WITH RECURSIVE
上的PostgreSQL文档,但是我对此应该去哪里以及如何构造相关查询感到有些困惑。
我已经做到了:
select s.id, b.story_id as blocker_id
from stories s
left outer join blockings b on s.id = b.blocked_story_id
where s.deleted_at is null
关于获取故事及其阻止者的列表,但是一些有关我需要加入/加入以获得预期结果的指针将很有帮助。
我想知道我可以先处理哪些故事。因此,我希望输出的内容按顺序包含所有故事,这样我就可以自上而下地工作,而从不遇到被阻止的故事。
blockings
表的内容为我提供了一个相互阻塞的故事之间的简单联接表。 story_id
是阻止者,blocked_story_id
是被阻止的人。
id | title ------------------ 1 | Story title 1 2 | Story title 2 3 | Story title 3 4 | Story title 4 5 | Story title 5
story_id | blocked_story_id --------------------------- 4 | 2 4 | 3 3 | 1 3 | 5
我希望看到以下结果:
id | title ------------------ 4 | Story title 4 2 | Story title 2 3 | Story title 3 1 | Story title 1 5 | Story title 5
答案 0 :(得分:0)
免责声明:由于我不清楚为什么您需要递归来查找被阻止的故事(SELECT blocked_story_id FROM blocking
可以轻松实现),请您提供更多信息。一个真正的递归案例可能是:“ 从故事4可以到达的所有阻止”或类似的东西。
据我所知,这是我所做的事情:
您的blocking
表说:故事4阻止了故事2和3。故事3阻止了故事1和5。因此,存在被阻止的故事1、2、3、5。由于递归,故事4可以阻止1和5到3。所以有两种方法来阻止它们(直接从起点3开始,从起点4到3)。我通过此查询给出了所有可能的路径:
WITH RECURSIVE blocks AS (
SELECT blocked_story_id, ARRAY[story_id]::int[] as path FROM blockings
UNION
SELECT bk.blocked_story_id, b.path || bk.story_id
FROM blockings bk INNER JOIN blocks b ON b.blocked_story_id = bk.story_id
)
SELECT b.blocked_story_id, s.title, b.path
FROM blocks b INNER JOIN stories s ON s.id = b.blocked_story_id;
结果:
blocked_story_id title path
2 Title 2 {4}
3 Title 3 {4}
1 Title 1 {3}
5 Title 5 {3}
1 Title 1 {4,3}
5 Title 5 {4,3}
答案 1 :(得分:0)
@ S-Man我想通了,这要归功于您的帮助,使我朝着正确的方向前进。
WITH recursive blockings_tree(id, title, path) AS (
SELECT stories.id, title, ARRAY[blockings.blocked_story_id, blockings.story_id]
FROM stories
LEFT OUTER JOIN blockings ON blockings.story_id = stories.id
UNION ALL
SELECT stories.id, stories.title, path || stories.id
FROM blockings_tree
JOIN blockings ON blockings.story_id = blockings_tree.id
JOIN stories ON blockings.blocked_story_id = stories.id
WHERE NOT blockings.blocked_story_id = any(path)
)
SELECT stories.*
FROM stories
JOIN (SELECT id, MAX(path) AS path FROM blockings_tree GROUP BY id) bt ON bt.id = stories.id
ORDER BY path