使用postgresql联接表进行递归树搜索

时间:2018-10-02 07:32:26

标签: sql postgresql tree

我有一个表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

2 个答案:

答案 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}

demo: db<>fiddle

答案 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