我希望用Postgres聚合一个JSON对象数组,特别是用于通过外键将关系列表返回到另一个表。在这种情况下,它是IDataService
和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,
...
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。
答案 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
子句中。