将聚合合并到单个数组而不是多行中

时间:2020-04-17 11:01:39

标签: sql json postgresql

通过以下查询,我几乎在结构上达到了我想要的:

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 个答案:

答案 0 :(得分:1)

首先:您的样本数据不适合您的INSERT语句。例如,您的city_idtext而不是integer值。在我的示例中,我使用了INSERT语句。

第二:如果不是真的,请不要存储数组。请规范化您的数据。存储数组有很多缺点:搜索值,索引值,联接值等确实很麻烦。在大多数情况下,您需要从一开始就取消嵌套这些值。


我不太确定两个嵌套的外部数组应该是什么,但是可以这样创建city / airports对象:

Click: 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. 创建一个具有id属性的机场JSON对象
  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