Как вернуть только разные значения из функции json.agg()

#json #postgresql

#json #postgresql

Вопрос:

У меня есть следующий запрос

 select json_agg(t) from (
select  json_build_object(
'field_1',a.field_1,
'field_2',a.field_2,
'field_4',json_agg(json_build_object('id',g.id,'data',g.fullname)),
'field_9',json_agg(json_build_object('id',i.optionid,'data',i.option))
 ) as data

from schema_1.tbl_342 a
left join schema_1.tbl_342_to_tbl_329_field_10 b on a.id=b.tbl_342_id
left join schema_1.tbl_329_customid c on b.tbl_329_id=c.id
left join schema_1.tbl_329_field_23_join d on c.id=d.id
left join schema_1.tbl_329_field_23 e on d.optionid = e.optionid
left join schema_1.tbl_342_to_tbl_312_field_4 f on a.id=f.tbl_342_id
left join schema_1.tbl_312_customid g on f.tbl_312_id = g.id
left join schema_1.tbl_342_field_9_join h on h.id=a.id
left join schema_1.tbl_342_field_9 i on i.optionid=h.optionid   
group by a.field_1,a.field_2
) t
  

Это приводит к следующему JSON формату

    [
  {
  "data":{
     "field_1":"John",
     "field_2":null,
     "field_4":[
        {
           "id":null,
           "data":null
        }
     ],
     "field_9":[
        {
           "id":2,
           "data":"Green"
        }
     ]
  }
 },
 {
  "data":{
     "field_1":"Jackson",
     "field_2":null,
     "field_4":[
        {
           "id":2,
           "data":"Marketing Manager M1004"
        },
        {
           "id":4,
           "data":"Senior Javascript Engineer"
        },
        {
           "id":5,
           "data":"Recruiter"
        }
     ],
     "field_9":[
        {
           "id":3,
           "data":"Red"
        },
        {
           "id":3,
           "data":"Red"
        },
        {
           "id":3,
           "data":"Red"
        }
     ]
  }
 },
  {
    "data":{
     "field_1":"Jacob",
     "field_2":null,
     "field_4":[
        {
           "id":null,
           "data":null
        }
     ],
     "field_9":[
        {
           "id":null,
           "data":null
        }
     ]
  }
 },
 {
  "data":{
     "field_1":"Todd",
     "field_2":null,
     "field_4":[
        {
           "id":null,
           "data":null
        }
     ],
     "field_9":[
        {
           "id":4,
           "data":"Yellow"
        }
     ]
  }
 },
 {
  "data":{
     "field_1":"Billy",
     "field_2":null,
     "field_4":[
        {
           "id":5,
           "data":"Recruiter"
        }
     ],
     "field_9":[
        {
           "id":1,
           "data":"Blue"
        }
     ]
   }
 }
 ]
  

В этом примере я пытаюсь исправить две вещи.

1. Удаление data элемента node. Я бы хотел, чтобы объекты начинались с root, а не в Data

2. Обратите внимание на второй узел. field_4 Имеет 3 элемента, но field_9 имеет единственное red значение в БД. Здесь он повторяет повторяющиеся значения, должно соответствовать количеству возвращенных записей из field_4

Как я могу получить только разные значения из этого агрегата? Я пытался

  'field_4',distinct json_agg(json_build_object('id',g.id,'data',g.fullname)),
  

и другие подобные формы, но им не нравится синтаксис.
К вашему сведению, я использую PostgreSQL 11

Ответ №1:

Ответы и решение вашей проблемы будут такими, как показано ниже:

Проблема 1 — Вы используете json_agg(t) для генерации окончательных JSON данных, которые будут выбирать имена всех столбцов или псевдонимы в качестве ключа для JSON и агрегировать их. Итак, здесь вы должны использовать array_to_json(array_agg(t.data)) . array_agg преобразует результат подзапроса в массив, а затем array_to_json преобразует конечный массив в JSON .

Проблема 2 — Вы хотите distinct json object во внутреннем массиве. Поэтому Distinct не может использоваться с json типом данных, потому что в equality operator для PostgreSQL типа нет JSON доступных. Поэтому вам следует использовать JSONB .

Учитывая, что ваш запрос, упомянутый в вопросе, работает нормально, попробуйте :

 select array_to_json(array_agg(t.data)) from (
select  jsonb_build_object(
'field_1',a.field_1,
'field_2',a.field_2,
'field_4',jsonb_agg(distinct jsonb_build_object('id',g.id,'data',g.fullname)),
'field_9',jsonb_agg(distinct jsonb_build_object('id',i.optionid,'data',i.option))
 ) as data

from schema_1.tbl_342 a
left join schema_1.tbl_342_to_tbl_329_field_10 b on a.id=b.tbl_342_id
left join schema_1.tbl_329_customid c on b.tbl_329_id=c.id
left join schema_1.tbl_329_field_23_join d on c.id=d.id
left join schema_1.tbl_329_field_23 e on d.optionid = e.optionid
left join schema_1.tbl_342_to_tbl_312_field_4 f on a.id=f.tbl_342_id
left join schema_1.tbl_312_customid g on f.tbl_312_id = g.id
left join schema_1.tbl_342_field_9_join h on h.id=a.id
left join schema_1.tbl_342_field_9 i on i.optionid=h.optionid   
group by a.field_1,a.field_2
) t
  

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

1. Спасибо! Это решило проблему дублирования. Другая проблема заключается в том, что верхний узел по-прежнему является «данными». Если я удалю «как данные», тогда верхний узел будет отображаться как «json_object_build». я хочу, чтобы узел имел формат вывода, подобный: {{«field_1»: «Билли», «field_2»: null, «field_4»: [{«id»: 4, «data»: «Старший инженер Javascript»}} . В настоящее время это {«data»:{«field_1»: «Billy», «field_2»: null, «field_4»: [{«id»: 4, «data»: «Старший инженер Javascript»}

2. Да, так и должно быть array_to_json(array_agg(t.data)) . я обновил ответ

3. каков результат этого? у ключей есть имя или это ‘f1’: значение?