Выполнить "конкатенацию json" в запросе postgres - PullRequest
0 голосов
/ 02 ноября 2018

Можно ли сделать что-то похожее на промежуточную сумму для JSON?

У меня есть эта таблица:

   day        id          data
────────────┼───────┼───────────────────
 2016-06-20 │     1 │ {"key0": "value0"}
 2016-06-21 │     1 │ {"key1": "value1"}
 2016-06-22 │     1 │ {"key2": "value2"}

И мне бы хотелось, чтобы это была таблица:

   day        id                  data
────────────┼───────┼────────────────────────────────────────────────────
 2016-06-20 │     1 │ {"key0": "value0"}
 2016-06-21 │     1 │ {"key0": "value0", "key1": "value1"}
 2016-06-22 │     1 │ {"key0": "value0", "key1": "value1", "key2": "value2"}

Я пытался использовать оконную функцию, потому что поведение по умолчанию похоже на это для агрегатных функций, но я не знаю, как правильно сделать это для json.

Кто-нибудь может помочь?

Ответы [ 2 ]

0 голосов
/ 02 ноября 2018

Нет встроенного агрегата, объединяющего объекты JSONB (jsonb_agg() возвращает массив, а не одно значение JSON), но его действительно легко создать:

create aggregate jsonb_append(jsonb) 
(
    sfunc = jsonb_concat(jsonb, jsonb),
    stype = jsonb
);

Этот агрегат также можно использовать как оконную функцию , которая выполняет «работающий агрегат», поэтому вы можете сделать:

select day, id, jsonb_append(data) over (order by day)
from topo
order by day;

Обратите внимание, что JSONB не сохраняет порядок ключей. Таким образом, порядок ключей внутри агрегированного значения jsonb может не совпадать с порядком поиска.

Если один и тот же ключ существует в нескольких строках, значение «последней» строки (согласно order by) будет сохранено.

Онлайн пример

0 голосов
/ 02 ноября 2018

Пока у вас есть способ определить, какие другие строки должны быть включены в «объединенные» данные каждой строки, это просто сделать с LATERAL.

testdb=# create table t(day date, id bigint, data jsonb);
ERROR:  relation "t" already exists
testdb=# select * from t;
    day     | id |        data        
------------+----+--------------------
 2016-06-20 |  1 | {"key0": "value0"}
 2016-06-21 |  1 | {"key1": "value1"}
 2016-06-22 |  1 | {"key2": "value2"}
(3 rows)

testdb=# SELECT t0.day,                                  
       t0.id,
       j
FROM t t0, LATERAL
(SELECT jsonb_object(keys, vals) j
 FROM (SELECT array_agg(kvset.key) keys,
              array_agg(kvset.value) vals
       FROM
   (SELECT key, value FROM t t1
    CROSS JOIN jsonb_each_text(t1.data) AS r
    WHERE t1.day<=t0.day) AS kvset
    ) AS kvpairs
)_;
    day     | id |                           j                            
------------+----+--------------------------------------------------------
 2016-06-20 |  1 | {"key0": "value0"}
 2016-06-21 |  1 | {"key0": "value0", "key1": "value1"}
 2016-06-22 |  1 | {"key0": "value0", "key1": "value1", "key2": "value2"}

В этом случае я использую t1.day<=t0.day, чтобы указать, что все строки, равные или меньшие заданной даты, должны быть отсканированы для построения комбинированного объекта для строки этой даты.

Стоит отметить, что я ничего не делаю, чтобы разумно обрабатывать конфликтующие ключи; это, вероятно, не делает правильную вещь, когда он встречает один и тот же ключ в нескольких строках.

...