[英]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.