将嵌套的 JSONB 数组连接成单个字符串

Posted

技术标签:

【中文标题】将嵌套的 JSONB 数组连接成单个字符串【英文标题】:Concatenating Nested JSONB Arrays into a Single String 【发布时间】:2020-01-04 09:09:02 【问题描述】:

在 Postgres 11 数据库中,有一个表 (traces),其中一列类型为 JSONB (trace)。 JSON 值始终是嵌套数组,格式为:

[ ["a", "b"], ... ]

数组的每一行中至少有一个子元素。我想添加第二列(已计算,但现在对于这种情况,一个简单的查询就足够了),它包含数组列的字符串表示形式,格式为

a.b c.d.e

来自[["a","b"],["c","d","e"]]的数组值。

我已经尝试了几件事,但我可能在这里遗漏了一些理论。在我看来,这将涉及某种双重聚合,一次用于每个嵌套数组,然后再次用于最外层数组。我如何在查询中表达这一点(如果这甚至是正确的方法)?

我的出发点是这个查询首先访问所有嵌套数组:

SELECT nested FROM traces, jsonb_array_elements(trace) nested;

它确实返回了一个嵌套数组列表,我认为nestedJSONB。我继续使用这样的方法:

SELECT
       trace,
       array_to_string(array_agg(nested), ' ')
FROM traces,
     jsonb_array_elements(trace) nested
GROUP BY trace;

但我遇到了无法“嵌套”聚合函数的问题。

【问题讨论】:

【参考方案1】:

demo:db<>fiddle

SELECT
    trace,
    string_agg(point_separated, ' ')                             -- 4
FROM (
    SELECT
        trace,
        string_agg(second_level, '.') AS point_separated         -- 3
    FROM
        traces,
        jsonb_array_elements(trace) as first_level,              -- 1
        jsonb_array_elements_text(first_level) as second_level   -- 2
    GROUP BY trace, first_level.value
) s
GROUP BY trace
    使用jsonb_array_elements() 将嵌套数组扩展为每个嵌套数组一条记录 通过第二次调用此函数,将嵌套数组的元素展开为每个元素一条记录。

目前的中间结果:

trace                         | value           | value
:---------------------------- | :-------------- | :----
[["a", "b"], ["c", "d", "e"]] | ["a", "b"]      | a    
[["a", "b"], ["c", "d", "e"]] | ["a", "b"]      | b    
[["a", "b"], ["c", "d", "e"]] | ["c", "d", "e"] | c    
[["a", "b"], ["c", "d", "e"]] | ["c", "d", "e"] | d    
[["a", "b"], ["c", "d", "e"]] | ["c", "d", "e"] | e    
[["e", "f", "g"], ["h", "i"]] | ["e", "f", "g"] | e    
[["e", "f", "g"], ["h", "i"]] | ["e", "f", "g"] | f    
[["e", "f", "g"], ["h", "i"]] | ["e", "f", "g"] | g    
[["e", "f", "g"], ["h", "i"]] | ["h", "i"]      | h    
[["e", "f", "g"], ["h", "i"]] | ["h", "i"]      | i 
    使用GROUP BYstring_agg() 将内部元素聚合成一个点分隔的字符串 使用 this 的第二次调用将这些结果聚合成一个以空格分隔的字符串。

如果聚合字符串的顺序对您很重要,则需要添加行数,因为如果您不告诉它们,像 string_agg() 这样的聚合不能保证一定的顺序。

jsonb_array_elements() 这样的设置返回函数支持添加这样一个行号的WITH ORDINALITY 扩展。这可用于将ORDER BY 添加到string_agg() 函数中:

demo:db<>fiddle

SELECT
    trace,
    string_agg(point_separated, ' ' ORDER BY number)
FROM (
    SELECT
        trace,
        first_level.number,
        string_agg(second_level.val, '.' 
             ORDER BY first_level.number, second_level.number) AS point_separated
    FROM
        traces,
        jsonb_array_elements(trace) WITH ORDINALITY as first_level(val, number),
        jsonb_array_elements_text(first_level.val) WITH ORDINALITY as second_level(val, number)
    GROUP BY trace, first_level.val, first_level.number
) s
GROUP BY trace

【讨论】:

谢谢,这正是我昨天得到的解决方案!我确实必须使用WITH ORDINALITY 来保留数组顺序。

以上是关于将嵌套的 JSONB 数组连接成单个字符串的主要内容,如果未能解决你的问题,请参考以下文章

读取 pyspark 数据框中的 jsonb 类型字段? [复制]

从 Ruby 数据哈希中提取值并将值连接成单个连续字符串

PHP在连接键时将嵌套数组转换为单个数组?

PostgreSQL jsonb-通过数组元素搜索字符串

plpgsql jsonb_set 用于带有嵌套数组的 JSON 对象数组

PostgreSQL:更新JSONB结构中嵌套数组中元素的属性