Ben*_*Ben 5 postgresql tree cte json recursive
具有这种简单的多对多自引用结构。
一个项目通过表拥有其他项目joins:
CREATE TABLE items (
item_id serial PRIMARY KEY
, title text
);
CREATE TABLE joins (
id serial PRIMARY KEY
, item_id int
, child_id int
);
INSERT INTO items (item_id, title) VALUES
(1, 'PARENT')
, (2, 'LEVEL 2')
, (3, 'LEVEL 3.1')
, (4, 'LEVEL 4.1')
, (5, 'LEVEL 4.2')
, (6, 'LEVEL 3.2')
;
INSERT INTO joins (item_id, child_id) VALUES
(1, 2)
, (2, 3)
, (3, 4)
, (3, 5)
, (2, 6)
;
Run Code Online (Sandbox Code Playgroud)
db<>在这里摆弄
我正在尝试以 JSON 形式检索给定项目的整个树结构。
例如查询值为item_id1的项目(伪代码):
SELECT i.*, fulltree from items i where item_id = 1;
Run Code Online (Sandbox Code Playgroud)
所需的输出fulltree:
{
id: 1,
title: "PARENT",
children: [
{
id: 2,
title: "LEVEL 2",
children: [
{
id: 3,
title: "LEVEL 3.1",
children: [
{
id: 4,
title: "LEVEL 4.1"
},
{
id: 5,
title: "LEVEL 4.2"
}
]
},
{
id: 6,
title: "LEVEL 3.2"
}
]
}
]
}
Run Code Online (Sandbox Code Playgroud)
在深入研究 Postgres 提供的 JSON 功能后,我通过重复嵌套查询来管理此类输出。简单但丑陋,并且限制重复次数。:/
我发现了递归查询。这里和那里找到的例子并不那么简单。很难找到一个切入点来理解该技术并使其适应我的需求。
我希望这里的示例足够简单,以便能够从有经验的用户那里获得帮助。
递归CTE (rCTE)不允许在递归项中进行聚合。所以没有简单的解决方案。
我建议使用递归函数来实现优雅的解决方案:
CREATE OR REPLACE FUNCTION f_item_tree(_item_id int)
RETURNS jsonb
LANGUAGE sql STABLE PARALLEL SAFE AS
$func$
SELECT jsonb_agg(sub)
FROM (
SELECT i.*, f_item_tree(i.item_id) AS children
FROM joins j
JOIN items i ON i.item_id = j.child_id
WHERE j.item_id = _item_id
ORDER BY i.item_id
) sub
$func$;
Run Code Online (Sandbox Code Playgroud)
裸调用:
SELECT to_jsonb(sub) AS tree
FROM (
SELECT *, f_item_tree(item_id) AS children
FROM items
WHERE item_id = 1 -- root item_id HERE
) sub;
Run Code Online (Sandbox Code Playgroud)
删除具有 NULL 值的对象(无子对象)并进行美化:
SELECT jsonb_pretty(jsonb_strip_nulls(to_jsonb(sub))) AS tree
FROM (
SELECT *, f_item_tree(item_id) AS children
FROM items
WHERE item_id = 1 -- root item_id HERE
) sub;
Run Code Online (Sandbox Code Playgroud)
准确地产生您想要的输出(一棵完整的树):
CREATE OR REPLACE FUNCTION f_item_tree(_item_id int)
RETURNS jsonb
LANGUAGE sql STABLE PARALLEL SAFE AS
$func$
SELECT jsonb_agg(sub)
FROM (
SELECT i.*, f_item_tree(i.item_id) AS children
FROM joins j
JOIN items i ON i.item_id = j.child_id
WHERE j.item_id = _item_id
ORDER BY i.item_id
) sub
$func$;
Run Code Online (Sandbox Code Playgroud)
递归地从给定 JSON 值中删除具有 null 值的所有对象字段。
如果null您想保留其他字段的值,则必须执行更多操作。喜欢:
CREATE OR REPLACE FUNCTION f_item_tree(_item_id int)
RETURNS jsonb
LANGUAGE sql STABLE PARALLEL SAFE AS
$func$
SELECT jsonb_agg(
CASE WHEN children IS NULL
THEN to_jsonb(sub) - 'children'
-- THEN jsonb_build_object('title', title, 'item_id', item_id) -- alt: spell out
ELSE to_jsonb(sub) END
)
FROM (
SELECT i.*, f_item_tree(i.item_id) AS children
FROM joins j
JOIN items i ON i.item_id = j.child_id
WHERE j.item_id = _item_id
ORDER BY i.item_id
) sub
$func$;
Run Code Online (Sandbox Code Playgroud)
后来,与替代方案密切相关的答案(最明显的是最大递归级别):
| 归档时间: |
|
| 查看次数: |
7183 次 |
| 最近记录: |