объединение более одной строки в один объект JSON на основе общего значения столбца

#arrays #json #postgresql #postgresql-9.6

#массивы #json #postgresql #postgresql-9.6

Вопрос:

У меня есть следующая таблица :

 id |   model    |  version   |   status
_________________________________________

.. |  model1    |   1.0      |    old
.. |  model1    |   2.0      |    new
.. |  model2    |   1.1      |    old
.. |  model2    |   1.2      |    new
 

Мне нужно сгенерировать объект JSON на основе статуса для той же модели (объединить две строки в один объект JSON), как показано ниже :

 [
   {
      "model" : "model1",
      "old_version" : "1.0",
      "new_version" : "2.0"
   },
   {
      "model" : "model2",
      "old_version" : "1.1",
      "new_version" : "1.2"
   }
]
 

Что я пробовал до сих пор :

 select json_agg(json_build_object(t."model",
                                  CASE WHEN t."status" == 'new'
                                   THEN 'new_version',t."version",
                                  end 
                                   CASE WHEN t."status" == 'old'
                                   THEN 'old_version',t."version",
                                  end ))
from (// my select query with various conditions goes here) t
 

Я также получаю синтаксическую ошибку. Как я могу добиться ожидаемого результата

Заранее благодарю!!

Ответ №1:

THEN Часть выражения CASE может содержать только одно выражение, поэтому then value_a, value_b end недопустима. Вам также не хватает ключа для model значения.

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

Это объединяет версии для каждой модели:

 select jsonb_build_object('model', model)||jsonb_object_agg(status||'_version', version)
from (...)
group by model
 

Если вы не можете или не хотите использовать имя состояния в качестве префикса для «ключа версии», вам придется использовать выражение CASE:

 select jsonb_build_object('model', model)|| 
              jsonb_object_agg(case status 
                                 when 'old' then 'old_version' 
                                 when 'new' then 'new_version' 
                               end, 
                               version)
from (...)
group by model
 

Затем это можно использовать в производной таблице для получения массива json:

 select jsonb_agg(model)
from (
  select jsonb_build_object('model', model)||jsonb_object_agg(status||'_version', version) as model
  from (...)
  group by model
) t
 

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

Комментарии:

1. Поскольку вы использовали оператор OR, не даст ли он два объекта JSON для каждой модели? Мне нужен один объект JSON со значениями версии и значением модели

2. @code-geek: я не понимаю, что вы имеете в виду. В моем запросе нет ИЛИ.

3. я имею в виду, что я не получаю ожидаемый массив JSON. Он добавляет дополнительный ключ в качестве модели, а значением является объект JSON с тремя ключами. Вместо этого я хочу массив с прямым объектом JSON с тремя ключами

4. Пожалуйста, посмотрите Онлайн-пример, он возвращает именно то значение, которое вы хотите

5. Это запрос, который я сформулировал. Объекты JSON создаются, как и ожидалось, но я сталкиваюсь с проблемой (добавлен дополнительный ключ) при выполнении агрегирования. Пожалуйста, помогите мне, где я делаю неправильно