6

由于 JSON 支持,我正在考虑切换到 PostgreSQL。但是,我想知道,如果单个查询可以实现以下操作:

假设有两个表:

表 1) 组织:

  ID (INT)  |  members (JSONB)                                        |
------------+---------------------------------------------------------|
     1      | [{ id: 23, role: "admin" }, { id: 24, role: "default" }]|
     2      | [{ id: 23, role: "user" }]

表 2) 用户:

  ID (INT)  | name TEXT | email TEXT    |
------------+-----------+---------------|
     23     | Max       | max@gmail.com |
     24     | Joe       | joe@gmail.com |

现在我想得到这样的结果(我只有组织的 ID [1]):

  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 中存储这些数据,但我只是好奇它是否可能。

谢谢!

4

2 回答 2

11

是的,使用 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
INNER JOIN LATERAL JSONB_ARRAY_ELEMENTS(o.data) AS e(usr) ON TRUE
INNER JOIN users u ON (e.usr->'id')::text::int = u.id
GROUP BY o.id

看到这个 db fiddle

解释 :

  • JSONB_ARRAY_ELEMENTS函数将组织 json 数组拆分为行(每个用户一个);它通常与JOIN LATERAL

  • 要加入users表格,我们使用运算符访问id字段的内容->

  • 对于每个用户,JSONB_BUILD_OBJECT通过传递值/键对列表来创建一个新对象;大多数值都来自users表,除了role,取自组织 json 元素

  • 查询按组织 ID 聚合,JSONB_AGG用于通过组合上述对象生成 json 数组

有关更多信息,您还可以查看Postgres JSON 函数文档

于 2019-01-24T00:36:16.827 回答
0

可能有更多方法可以做到这一点。一种方法是将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";

db<>小提琴

具体有哪些功能取决于版本。但是既然你说你考虑切换,假设一个更新的版本应该是公平的。

于 2019-01-24T00:33:05.787 回答