繁体   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