PostgreSQL 可以在 jsonb 数组对象上加入吗?
Can PostgreSQL JOIN on jsonb array objects?
我正在考虑切换到 PostgreSQL,因为 JSON 支持。但是,我想知道,是否可以通过单个查询执行以下操作:
假设有两个表:
Table 1) 组织:
ID (INT) | members (JSONB) |
------------+---------------------------------------------------------|
1 | [{ id: 23, role: "admin" }, { id: 24, role: "default" }]|
2 | [{ id: 23, role: "user" }]
Table 2) 用户:
ID (INT) | name TEXT | email TEXT |
------------+-----------+---------------|
23 | Max | max@gmail.com |
24 | Joe | joe@gmail.com |
现在我想得到这样的结果(我只有组织 [1] 的 ID):
ID (INT) | members (JSONB) |
------------+--------------------------------------------------------|
1 | [{ id: 23, name: "Max", email: "max@gmail.com", role:
"admin" },
{ id: 24, name: "Joe", email: "joe@gmail.com ", role:
"default" }]
(1 row)
我知道这不是 JSONB 的目的,并且有更好的解决方案可以将此数据存储在 SQL 中,但我只是想知道这是否可行。
谢谢!
可能有更多方法可以做到这一点。一种方法是使用 jsonb_to_recordset()
将 JSON 转换为您可以加入的记录集。然后使用 jsonb_build_object()
为单个成员创建 JSON 并使用 jsonb_agg()
将它们聚合到 JSON 数组中。
SELECT jsonb_agg(jsonb_build_object('id', "u"."id",
'name', "u"."name",
'email', "u"."email",
'role', "m"."role"))
FROM "organisations" "o"
CROSS JOIN LATERAL jsonb_to_recordset(o."members") "m" ("id" integer,
"role" text)
INNER JOIN "users" "u"
ON "u"."id" = "m"."id";
具体提供哪些功能取决于版本。但是既然你说你考虑切换,假设更新的版本应该是公平的。
是的,使用 Postgres 可以满足此要求。这是 9.6 或更高版本的解决方案。
SELECT o.id, JSON_AGG(
JSON_BUILD_OBJECT(
'id' , u.id,
'name' , u.name,
'email' , u.email,
'role' , e.usr->'role'
)
)
FROM organisations o
CROSS JOIN LATERAL JSONB_ARRAY_ELEMENTS(o.data) AS e(usr)
INNER JOIN users u ON (e.usr->'id')::text::int = u.id
GROUP BY o.id
解释:
JSONB_ARRAY_ELEMENTS
函数将组织 json 数组拆分成行(每个用户一个);它通常与 JOIN LATERAL
结合使用
加入users
table,我们使用->
运算符
访问id
字段的内容
对于每个用户,JSONB_BUILD_OBJECT
用于通过传递 values/keys 对列表来创建新对象;大多数值来自 users
table,但 role
除外,它取自组织 json 元素
查询按组织 ID 聚合,使用 JSONB_AGG
通过组合上述对象生成 json 数组
更多信息,您还可以查看Postgres JSON Functions documentation。
我正在考虑切换到 PostgreSQL,因为 JSON 支持。但是,我想知道,是否可以通过单个查询执行以下操作:
假设有两个表:
Table 1) 组织:
ID (INT) | members (JSONB) |
------------+---------------------------------------------------------|
1 | [{ id: 23, role: "admin" }, { id: 24, role: "default" }]|
2 | [{ id: 23, role: "user" }]
Table 2) 用户:
ID (INT) | name TEXT | email TEXT |
------------+-----------+---------------|
23 | Max | max@gmail.com |
24 | Joe | joe@gmail.com |
现在我想得到这样的结果(我只有组织 [1] 的 ID):
ID (INT) | members (JSONB) |
------------+--------------------------------------------------------|
1 | [{ id: 23, name: "Max", email: "max@gmail.com", role:
"admin" },
{ id: 24, name: "Joe", email: "joe@gmail.com ", role:
"default" }]
(1 row)
我知道这不是 JSONB 的目的,并且有更好的解决方案可以将此数据存储在 SQL 中,但我只是想知道这是否可行。
谢谢!
可能有更多方法可以做到这一点。一种方法是使用 jsonb_to_recordset()
将 JSON 转换为您可以加入的记录集。然后使用 jsonb_build_object()
为单个成员创建 JSON 并使用 jsonb_agg()
将它们聚合到 JSON 数组中。
SELECT jsonb_agg(jsonb_build_object('id', "u"."id",
'name', "u"."name",
'email', "u"."email",
'role', "m"."role"))
FROM "organisations" "o"
CROSS JOIN LATERAL jsonb_to_recordset(o."members") "m" ("id" integer,
"role" text)
INNER JOIN "users" "u"
ON "u"."id" = "m"."id";
具体提供哪些功能取决于版本。但是既然你说你考虑切换,假设更新的版本应该是公平的。
是的,使用 Postgres 可以满足此要求。这是 9.6 或更高版本的解决方案。
SELECT o.id, JSON_AGG(
JSON_BUILD_OBJECT(
'id' , u.id,
'name' , u.name,
'email' , u.email,
'role' , e.usr->'role'
)
)
FROM organisations o
CROSS JOIN LATERAL JSONB_ARRAY_ELEMENTS(o.data) AS e(usr)
INNER JOIN users u ON (e.usr->'id')::text::int = u.id
GROUP BY o.id
解释:
结合使用JSONB_ARRAY_ELEMENTS
函数将组织 json 数组拆分成行(每个用户一个);它通常与JOIN LATERAL
加入
访问users
table,我们使用->
运算符id
字段的内容对于每个用户,
JSONB_BUILD_OBJECT
用于通过传递 values/keys 对列表来创建新对象;大多数值来自users
table,但role
除外,它取自组织 json 元素查询按组织 ID 聚合,使用
JSONB_AGG
通过组合上述对象生成 json 数组
更多信息,您还可以查看Postgres JSON Functions documentation。