簡體   English   中英

如何使用 Postgres 聚合一組 JSON 對象?

[英]How to aggregate an array of JSON objects with Postgres?

我希望使用 Postgres 聚合一組 JSON 對象,專門用於通過外鍵將關系列表返回到另一個表。 在這種情況下,它是user和他們的teams

這是我正在使用的架構:

CREATE TABLE teams (
  id TEXT PRIMARY KEY,
  ...
);

CREATE TABLE users (
  id TEXT PRIMARY KEY,
  ...
);

CREATE TABLE memberships (
  id TEXT PRIMARY KEY,
  user_id TEXT NOT NULL FOREIGN KEY (user_id) REFERENCES users(id),
  team_id TEXT NOT NULL FOREIGN KEY (team_id) REFERENCES teams(id)
);

使用以下查詢:

  SELECT
    users.id,
    ...
    CASE
      WHEN count(teams.*) = 0
      THEN '[]'::JSON
      ELSE json_agg(DISTINCT teams.id)
    END AS teams
  FROM users
  LEFT JOIN memberships ON users.id = memberships.user_id
  LEFT JOIN teams ON teams.id = memberships.team_id
  WHERE users.id = $[userId]
  GROUP BY
    users.id,
    ...

我可以將結果作為team_id的平面數組:

{
  id: 'user_1',
  ...
  teams: ['team_1', 'team_2']
}

但我想將結果作為 JSON 對象接收:

{
  id: 'user_1',
  ...
  teams: [
    { id: 'team_1' },
    { id: 'team_2' }
  ]
}

我非常接近:

  SELECT
    users.id,
    ...
    CASE
      WHEN count(teams.*) = 0
      THEN '[]'::JSON
      ELSE json_agg(json_build_object('id', teams.id))
    END AS teams
  FROM users
  LEFT JOIN memberships ON users.id = memberships.user_id
  LEFT JOIN teams ON teams.id = memberships.team_id
  WHERE users.id = $[userId]
  GROUP BY
    users.id,
    ...

但是現在我已經失去了DISTINCT函數的重復結果刪除功能,所以我最終為每個team返回了重復的 ID。

您可以使用選擇適當組合的子查詢來解決這個問題,然后聚合到一個json數組中:

SELECT id, json_strip_nulls(json_agg(json_build_object('id', team))) AS teams
FROM (
  SELECT DISTINCT user_id AS id, team_id AS team
  FROM memberships
  WHERE user_id = $[userId]) sub
GROUP BY id;

您可以從memberships表中獲取用戶ID 和團隊ID,因此將任一表連接到memberships表都沒有意義(除非您從這些表中獲取了尚未向我們展示的其他字段)。 如果您確實想使用其他字段,您可以將JOIN重新粘貼回來。

json_strip_nulls()函數將去掉[{"id": null}]出現並用空的[]::json替換它們。 這是 PG 9.5 的新功能。 這也擺脫了相當丑陋和低效的CASE子句。

在我看來,這會做到:

SELECT  json_build_object(
          'id',    u.id,
          'teams', array_remove(array_agg(DISTINCT t.*), NULL))
FROM    users u
LEFT OUTER JOIN memberships m
ON      m.user_id = u.id
LEFT OUTER JOIN teams t
ON      m.team_id = t.id
GROUP BY u.id

在 9.4 中工作。 對於沒有團隊的用戶來說,刪除NULL的部分是必要的。

我懷疑在 Postgres 中做 JSON 的一個很好的一般原則是盡可能長時間地堅持使用數組和記錄,並且只在最后時刻切換到 JSON。 更傳統的結構存在的時間更長並且與關系模型的聯系更緊密,因此您在使用它們時遇到問題的可能性較小。 你可以看到這個查詢可能有很容易地返回指定的列id和數組值列命名的teams

請注意,此查詢為所有用戶提供。 如果你只想要一個,把它放在WHERE子句中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM