如何使用Postgres聚合JSON对象数组?

时间:2016-03-11 20:40:23

标签: sql json postgresql

我希望用Postgres聚合一个JSON对象数组,特别是用于通过外键将关系列表返回到另一个表。在这种情况下,它是IDataServiceuser

这是我正在使用的架构:

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, ... s:

的平面数组得到
team_id

但我希望以JSON对象的形式收到结果:

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

我非常接近:

{
  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返回了重复的ID。

2 个答案:

答案 0 :(得分:3)

您可以使用选择适当组合的子查询来解决此问题,然后汇总到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表(除非您从这些表中获取其他字段#39; t向我们展示)。如果您确实想要使用其他字段,可以将JOIN重新粘贴回来。

json_strip_nulls()函数将摆脱[{"id": null}]次出现,并将其替换为空[]::json。这是PG 9.5的新功能。这也消除了相当丑陋和低效的CASE条款。

答案 1 :(得分:2)

在我看来,这样做会:

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子句中。