Postgres无限自我加入
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Postgres无限自我加入相关的知识,希望对你有一定的参考价值。
所以我有一篇文章和文章的“评论”..
评论允许人们回复..你可以回复答案..等等,这意味着最深的树根将是N
快速模拟表格的样子
Comments(id, news_id, user_id, body, likes)
Replies(id, parent_id) --> id here is = Comments.id
User(id, username, password)
News(id, title, body, image)
有没有办法查询Postgres DB给我一些类似的结果
因此,Replies
表中具有null parent_id的任何内容都是“主要”注释(也就是不是回复)..如果children
字段在其自身内填充(即回复的回复),我会很高兴
Postgres甚至可以实现这一点吗?或者我应该拿起所有Replies
加入他们与Comments
然后迭代通过每一个试图找到它适当的desitination?
顺便说一句,我正在使用GoLang
作为我的后端和Gorm
包来访问我的postgres数据库
编辑:我正在使用此查询
with recursive commentss as (
select r.id, r.parent, array[r.id] as all_parents,
c.body, u.username
from replies r
inner join comments c
on c.id = r.id
join users u
on u.id = c.user_refer
where (parent <> '') IS NOT TRUE
union all
select r.id, r.parent, c.all_parents || r.id,
co.body, u.username
from replies r
join comments co
on co.id = r.id
join users u
on u.id = co.user_refer
join commentss c
on r.parent = c.id
and r.id <> ALL (c.all_parents)
)
select * from commentss order by all_parents;
结果是:
这是更接近的..然而我需要的是返回一个看起来像JSON对象
comments: [
{
comment_id: ...,
username: ...,
comment_body: ....,
comment_likes: ....,
children: [...]
},
{
.....
}
]
comments
对象中的第一个项目将是不是回复的注释,而children
字段应该填充回复的注释..并且children
内的注释也应该填充children
以回复该回复
希望这是你的预期结果。 (我在这里做了类似的事情:https://stackoverflow.com/a/52076212/3984221)
表comments
:
id body user_id likes
-- ------------ ------- -----
a foo 1 1
b foofoo 1 232
c foofoofoo 1 23232
d fooFOO 1 53
e cookies 1 864
f bar 1 44
g barbar 1 54
h barBAR 1 222
i more cookies 1 1
表replies
id parent_id
-- ---------
a (null)
b a
c b
d a
e (null)
f (null)
g f
h f
i (null)
结果:
{
"comments": [{
"children": [],
"username": "Mike Tyson",
"comment_id": "i",
"comment_body": "more cookies",
"comment_likes": 1
},
{
"children": [{
"children": [],
"username": "Mike Tyson",
"comment_id": "b",
"comment_body": "foofoo",
"comment_likes": 232
},
{
"children": [{
"children": [],
"username": "Mike Tyson",
"comment_id": "c",
"comment_body": "foofoofoo",
"comment_likes": 23232
}],
"username": "Mike Tyson",
"comment_id": "d",
"comment_body": "fooFOO",
"comment_likes": 53
}],
"username": "Mike Tyson",
"comment_id": "a",
"comment_body": "foo",
"comment_likes": 1
},
{
"children": [],
"username": "Mike Tyson",
"comment_id": "e",
"comment_body": "cookies",
"comment_likes": 864
},
{
"children": [{
"children": [],
"username": "Mike Tyson",
"comment_id": "g",
"comment_body": "barbar",
"comment_likes": 54
},
{
"children": [],
"username": "Mike Tyson",
"comment_id": "h",
"comment_body": "barBAR",
"comment_likes": 222
}],
"username": "Mike Tyson",
"comment_id": "f",
"comment_body": "bar",
"comment_likes": 44
}]
}
查询:
递归:
WITH RECURSIVE parent_tree AS (
SELECT
id,
NULL::text[] as parent_id,
array_append('{comments}'::text[], (row_number() OVER ())::text) as path,
rc.children
FROM replies r
LEFT JOIN LATERAL (SELECT parent_id, ARRAY_AGG(id) as children FROM replies WHERE parent_id = r.id GROUP BY parent_id) rc ON rc.parent_id = r.id
WHERE r.parent_id IS NULL
UNION
SELECT
r.id,
array_append(pt.parent_id, r.parent_id),
array_append(array_append(pt.path, 'children'), (row_number() OVER (PARTITION BY pt.parent_id))::text),
rc.children
FROM parent_tree pt
JOIN replies r ON r.id = ANY(pt.children)
LEFT JOIN LATERAL (SELECT parent_id, ARRAY_AGG(id) as children FROM replies WHERE parent_id = r.id GROUP BY parent_id) rc ON rc.parent_id = r.id
), json_objects AS (
SELECT c.id, jsonb_build_object('children', '[]'::jsonb, 'comment_id', c.id, 'username', u.name, 'comment_body', c.body, 'comment_likes', c.likes) as jsondata
FROM comments c
LEFT JOIN users u ON u.id = c.user_id
)
SELECT
parent_id,
path,
jsondata
FROM parent_tree pt
LEFT JOIN json_objects jo ON pt.id = jo.id
ORDER BY parent_id NULLS FIRST
唯一的递归部分是在CTE parent_tree
内。在这里,我正在寻找父母并建立一条道路。稍后在正确的位置插入json数据需要此路径。
第二个CTE(json_objects
)为每个注释构建一个json对象,其中包含一个空子数组,以便稍后插入子项。
LATERAL
join在回复表中搜索当前id的子项,并给出一个带有id的数组。
最后的ORDER BY
条款很重要。通过这种方式,可以确保所有上层节点都位于下层节点(它们的子节点)之前。否则,对全局json对象的输入可能会在以后失败,因为必要的父级在适当的时刻不能存在。
构建最终的JSON对象:
CREATE OR REPLACE FUNCTION json_tree() RETURNS jsonb AS $$
DECLARE
_json_output jsonb;
_temprow record;
BEGIN
SELECT
jsonb_build_object('comments', '[]'::jsonb)
INTO _json_output;
FOR _temprow IN
-- <query above>
LOOP
SELECT jsonb_insert(_json_output, _temprow.path, _temprow.jsondata) INTO _json_output;
END LOOP;
RETURN _json_output;
END;
$$ LANGUAGE plpgsql;
无法在递归中构建json对象,因为在查询中jsondata
对象不是全局变量。因此,如果我在一个递归分支中将b
作为子项添加到a
中,它将不存在于另一个分支中,我将c
添加为子项。
因此有必要生成一个全局变量。这可以在一个函数中完成。使用计算出的路径和子对象,将最终的json组合在一起非常简单:循环遍历结果集并将json对象添加到全局对象的路径中。
以上是关于Postgres无限自我加入的主要内容,如果未能解决你的问题,请参考以下文章
Python web 服务来自我的 Postgres DB 的 JSON 提要
Postgres RDS 数据库数据库连接在星期六无限增加,导致 Spring Boot Java API 应用程序中出现“JDBCConnectionException”