我在Postgres中有一个存储树结构的表。每个节点都有一个jsonb
字段:params_diff
:
CREATE TABLE tree (id INT, parent_id INT, params_diff JSONB);
INSERT INTO tree VALUES
(1, NULL, '{ "some_key": "some value" }'::jsonb)
, (2, 1, '{ "some_key": "other value", "other_key": "smth" }'::jsonb)
, (3, 2, '{ "other_key": "smth else" }'::jsonb);
我需要的是按id
选择一个节点,其中包含额外生成的params
字段,该字段包含合并来自整个父链的所有params_diff
的结果:
SELECT tree.*, /* some magic here */ AS params FROM tree WHERE id = 3;
id | parent_id | params_diff | params
----+-----------+----------------------------+-------------------------------------------------------
3 | 2 | {"other_key": "smth else"} | {"some_key": "other value", "other_key": "smth else"}
答案 0 :(得分:3)
通常,recursive CTE可以完成这项工作。例如:
我们需要更多魔法来分解,处理和重新组装JSON结果。我假设从你的例子中,你只需要每个键一次,搜索路径中的第一个值(自下而上):
WITH RECURSIVE cte AS (
SELECT id, parent_id, params_diff, 1 AS lvl
FROM tree
WHERE id = 3
UNION ALL
SELECT t.id, t.parent_id, t.params_diff, c.lvl + 1
FROM cte c
JOIN tree t ON t.id = c.parent_id
)
SELECT id, parent_id, params_diff
, (SELECT json_object(array_agg(key ORDER BY lvl)
, array_agg(value ORDER BY lvl))::jsonb
FROM (
SELECT key, value
FROM (
SELECT DISTINCT ON (key)
p.key, p.value, c.lvl
FROM cte c, jsonb_each_text(c.params_diff) p
ORDER BY p.key, c.lvl
) sub1
ORDER BY lvl
) sub2
) AS params
FROM cte
WHERE id = 3;
LATERAL JOIN
中使用jsonb_each_text()
创建包含所有键和值的派生表,记住搜索路径中的级别(lvl
)。DISTINCT ON
获取每个lvl
的“第一个”(最低value
)key
。细节:
json_object()
以构建最终的params
值。 SQL Fiddle(只有第9.3页可以使用json
代替jsonb
)。