Как получить ответ в желаемом формате из PostgreSQL

#sql #arrays #json #postgresql #aggregate-functions

#sql #массивы #json #postgresql #агрегатные функции

Вопрос:

У меня есть три таблицы. Курс, темы, подтемы. Все они содержат соответствующее имя, идентификатор, описание и т.д. Мой запрос заключается в получении всех данных из базы данных.

 select course.course_id, course_name, course_description,
   topic.topic_id, topic_name, topic_description,
   subtopic.subtopic_id, subtopic_name 
from 
course join topic on course.course_id = topic.course_id    
join subtopic on topic.topic_id = subtopic.topic_id
order by course.course_id, topic.topic_id, subtopic.subtopic_id;
  

Формат, который я получаю, представляет собой массив объектов. Желаемый результат

 course:[
     {
       course_id:1,
       topics:[
          {
            topic_id:1,
            subtopics:[
                        subtopic_id:1
                      ]
          }
       ]
     },
     ...
     ...
     ...
]
  

Есть ли способ запросить мою базу данных таким образом, чтобы мой вывод был в этом формате? Подход Javascript к оптимальному форматированию данных в таком формате также приветствуется.

Ответ №1:

Вы могли бы использовать агрегацию JSON:

 select c.course_id, c.course_name, c.course_description,
    jsonb_agg(
        jsonb_build_object(
            'topic_id',          t.topic_id,
            'topic_name',        t.topic_name,
            'topic_description', t.topic_description,
            'subtopics',         st.subtopics
        )
        order by t.topic_id
    ) topics 
from course c
inner join topic t on c.course_id = t.course_id    
cross join lateral (
    select jsonb_agg(
        jsonb_build_object(
            'subtopic_id',   st.subtopic_id, 
            'subtopic_name', st.subtopic_name
        )
        order by st.subtopic_id
    ) subtopics
    from subtopic st 
    where st.topic_id = t.topic_id
) st
group by c.course_id
order by c.course_id
  

Вы можете добавить еще один уровень агрегации, если хотите получить скалярный результат из большого объекта JSONB, как показано в вашем результирующем наборе:

 select jsonb_build_object(
    'courses',
    jsonb_agg(
        jsonb_build_object(
            'course_id',          course_id,
            'course_name',        course_name
            'course_description', course_description
            'topics',             topics
        )
    )
    order by order by course_id
) result
from (
    select c.course_id, c.course_name, c.course_description,
        jsonb_agg(
            jsonb_build_object(
                'topic_id',          t.topic_id,
                'topic_name',        t.topic_name,
                'topic_description', t.topic_description,
                'subtopics',         st.subtopics
            )
            order by t.topic_id
        ) topics 
    from course c
    inner join topic t on c.course_id = t.course_id    
    cross join lateral (
        select jsonb_agg(
            jsonb_build_object(
                'subtopic_id',   st.subtopic_id, 
                'subtopic_name', st.subtopic_name
            )
            order by st.subtopic_id
        ) subtopics
        from subtopic st 
        where st.topic_id = t.topic_id
    ) st
    group by c.course_id
) t
  

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

1. @eras’q: есть ли причина, по которой вы не приняли этот ответ?

2. Я снова просмотрел ваш запрос, и его было трудно понять. Не могли бы вы добавить некоторые пояснения? Кроме того, можем ли мы оптимизировать приведенный выше запрос?