我有一个 PostgreSQL 10 表,它充当“字典”,其结构如下:
现在我想知道是否有一种方法可以使用表中的值构建 JSON,并根据“key”的值构建层次结构? 像这样的东西:
style --> selection --> color
和
style --> line --> color
以 JSON 结束:
{
style: [
selection: {
color: "...",
weight: "..."
},
line: {
color: "...",
weight: "..."
}
]
}
这样的壮举可以实现吗?如果是这样,我该怎么办?
是否可以这样做,以便无论我的表中有什么键,它总是返回正确构建的 JSON?
提前致谢
最佳答案
PosGres 10 及更高版本的工作解决方案
我向您建议一个通用解决方案,将key
数据转换为text[]
类型,以便它可以用作jsonpath
内部标准 jsonb_set()
函数。
但是当我们迭代 jsonb_set()
函数时,我们首先需要创建一个与该函数关联的 aggregate
函数:
CREATE AGGREGATE jsonb_set_agg(p text[], z jsonb, b boolean)
( sfunc = jsonb_set
, stype = jsonb
, initcond = '{}'
)
然后,我们将 key
数据转换为 text[]
,并自动生成 jsonpath
列表,该列表将允许逐步迭代构建最终的 jsonb 数据:
SELECT i.id
, max(i.id) OVER (PARTITION BY t.key) AS id_max
, p.path[1 : i.id] AS jsonbpath
, to_jsonb(t.value) AS value
FROM mytable AS t
CROSS JOIN LATERAL string_to_array(t.key, '_') AS p(path)
CROSS JOIN LATERAL generate_series(1, array_length(p.path, 1)) AS i(id)
最终查询如下所示:
WITH list AS
( SELECT i.id
, max(i.id) OVER (PARTITION BY t.key) AS id_max
, p.path[1 : i.id] AS jsonpath
, to_jsonb(t.value) AS value
FROM mytable AS t
CROSS JOIN LATERAL string_to_array(t.key, '_') AS p(path)
CROSS JOIN LATERAL generate_series(1, array_length(p.path, 1)) AS i(id)
)
SELECT jsonb_set_agg( l.jsonpath
, CASE
WHEN l.id = l.id_max THEN l.value
ELSE '{}' :: jsonb
END
, true
ORDER BY l.id
)
FROM list AS l
结果与您的预期略有不同(顶级 json 数组被 json 对象替换),但对我来说听起来更有逻辑:
{"style": {"line": {"color": "C"
, "weight": "D"
}
, "selection": {"color": "A"
, "weight": "B"
}
}
}
完整测试结果在 dbfiddle .
关于sql - 使用 PostgreSQL10 根据行中的值分层聚合 JSON,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70894367/