Sql 如何使用Postgres聚合JSON对象数组?
我希望用Postgres聚合一个JSON对象数组,专门用于通过外键将关系列表返回到另一个表。在这种情况下,它是Sql 如何使用Postgres聚合JSON对象数组?,sql,json,postgresql,Sql,Json,Postgresql,我希望用Postgres聚合一个JSON对象数组,专门用于通过外键将关系列表返回到另一个表。在这种情况下,它是用户及其团队 以下是我正在使用的模式: CREATE TABLE teams ( id TEXT PRIMARY KEY, ... ); CREATE TABLE users ( id TEXT PRIMARY KEY, ... ); CREATE TABLE memberships ( id TEXT PRIMARY KEY, user_id TEXT NO
用户
及其团队
以下是我正在使用的模式:
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\u id
s的平面数组获得结果:
{
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
函数的结果重复消除功能,因此我最终为每个团队返回了重复的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,也可以从memberships
表中获取团队id,因此将任何一个表连接到memberships
表都没有意义(除非您从那些表中获取了其他字段,您还没有向我们展示)。如果您确实想使用其他字段,可以将JOIN
s右键粘贴回
json\u strip\u nulls()
函数将删除出现的[{“id”:null}]
事件,并将它们替换为空的[]::json
。这是第9.5页的新功能。这也摆脱了相当丑陋和低效的案例
条款。在我看来,这样做可以:
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
s的部分对于没有团队的用户是必需的
我认为在Postgres中使用JSON的一个好的一般原则是尽可能长时间地使用数组和记录,并且只在最后一刻切换到JSON。更传统的结构存在的时间更长,并且与关系模型的联系更紧密,因此使用它们时不太可能遇到问题。您可以看到,此查询可以同样轻松地返回名为id
的列和名为teams
的数组值列
注意:此查询提供所有用户。如果您只想要一个,请将其放在
WHERE
子句中。您需要一个带有array_to_json的子查询,请参见这里的最后一个示例:使用coalesce
似乎会导致在仍然没有匹配的团队时返回[{id:null}]
,因为会员资格
也有我需要的信息!因此,您可以拥有一个没有团队的用户。好的如果一个用户至少有一个成员资格,他是否仍然可以拥有一个没有团队的条目?如(用户id,团队id)=[(1,null),(1,14),(1,26)]
中所述,或者这是不可能的?如果您使用第9.5页,请参阅更新的答案。(如果没有,升级.PG 9.5json
支持将大大扩展。)