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

时间:2019-08-31 12:48:04

标签: arrays postgresql aggregate-functions jsonb

在Postgres 11数据库中,有一个表(traces)和一列类型为JSONBtrace)的列。 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 个答案:

答案 0 :(得分: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
  1. 使用jsonb_array_elements()将嵌套数组扩展为每个嵌套数组一个记录
  2. 第二次调用此函数,将嵌套数组的元素扩展为每个元素一个记录。

到目前为止的中间结果:

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 
  1. 使用GROUP BYstring_agg()将内部元素聚合为点分隔的字符串
  2. 使用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