Объединение агрегатов в один массив, а не в несколько строк - PullRequest
0 голосов
/ 17 апреля 2020

С помощью следующего запроса я почти достиг желаемого с точки зрения структуры:

SELECT
    t.id,
    t.name,
    json_agg(places) AS places
FROM
    trips t

    -- trips_cities
    INNER JOIN (
        SELECT
            tc.city_id,
            tc.trip_id,
            json_agg(json_build_object(
                'city_id', tc.city_id,
                'airports', airports
            )) AS places
        FROM
            trips_cities tc

            -- airports 
            LEFT JOIN (
                SELECT
                    a.id,
                    a.country_id,
                    json_agg(json_build_object(
                        'airport_id', a.id
                    )) AS airports
                FROM airports a
                GROUP BY a.id
            ) a ON a.id = ANY(tc.airport_ids)
            -- /airports

        GROUP BY 1, 2
    ) tc ON tc.trip_id = t.id
    -- /trips_cities

GROUP BY 1

Однако вместо аэропортов, являющихся массивом, он возвращает дубликаты городов:

[
  [
    {
      "city_id": 20,
      "airports": [
        {
          "airport_id": 2
        }
      ]
    },
    {
      "city_id": 20,
      "airports": [
        {
          "airport_id": 1
        }
      ]
    }
  ]
]

Вместо того, как мне хотелось бы, что я не могу определить для группировки:

[
  [
    {
      "city_id": 20,
      "airports": [
        {
          "airport_id": 2
        },
        {
          "airport_id": 1
        }
      ]
    }
  ]
]

Некоторые данные:

INSERT INTO trips (id, name)
VALUES (1, 'My First Trip'),
       (2, 'My Second Trip');

INSERT INTO trips_cities (trip_id, city_id, airport_ids)
VALUES (1, 'London', {1,2}),
       (2, 'Paris', {1}),    
       (3, 'Berlin', {2});     

INSERT INTO airports(id, name)
VALUES (1, 'Heathrow'),
       (2, 'Gatwick');

Табличные структуры следующие:

поездки

  • id

trips_cities

  • trip_id
  • city_id
  • airport_ids[]

аэропорты

  • id

В итоге:

  • Для каждой поездки присоединяйтесь trips_cities
  • Для каждой trips_cities присоединяйтесь ко всем airports
  • Создайте вложенную агг результаты, как в моем последнем JSON примере выше

1 Ответ

1 голос
/ 17 апреля 2020

Первый: ваши данные образца не соответствуют вашим INSERT заявлениям. Например, ваши city_id s text вместо integer значений. В моем примере я использовал операторы INSERT.

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


Я не совсем уверен, какими должны быть два вложенных внешних массива, но объект city / airport можно создать следующим образом:

Нажмите: demo: db <> fiddle

SELECT
    trip_id,
    json_build_object(                     -- 4
        'city_id', city_id,
        'airports', json_agg(json_build_object('airport_id', airport_id)) -- 2,3
    )
FROM trips_cities,
   unnest(airport_ids) as airport_id       -- 1
GROUP BY trip_id, city_id                  -- 3
  1. удалите ваши массивы, чтобы получить один элемент на строку
  2. , создав аэропорт JSON объект с атрибутом id
  3. агрегирование аэропортов для поездок и городов
  4. создание объекта города / аэропортов из данных города и массива аэропортов JSON.

Если вам нужны данные из других таблиц, например название аэропорта, вы можете присоединиться к ним:

SELECT
    trip_id,
    json_build_object(
        'city_id', city_id,
        'airports', json_agg(
            json_build_object(
                'airport_id', airport_id,
                'name', a.name
            )
        )
    )
FROM trips_cities,
   unnest(airport_ids) as airport_id
JOIN airports a ON a.id = airport_id
GROUP BY trip_id, city_id
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...