【问题标题】:Can PostgreSQL JOIN on jsonb array objects?PostgreSQL 可以加入 jsonb 数组对象吗?
【发布时间】:2019-06-17 14:25:12
【问题描述】:

由于 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 |

现在我想得到这样的结果(我只有组织 [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 中,但我只是好奇它是否可能。

谢谢!

【问题讨论】:

    标签: sql arrays json postgresql lateral-join


    【解决方案1】:

    是的,使用 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
    

    this db fiddle

    解释:

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

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

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

    • 查询按组织id聚合,使用JSONB_AGG结合以上对象生成json数组

    更多信息,您还可以查看Postgres JSON Functions documentation

    【讨论】:

    • INNER JOIN LATERAL JSONB_ARRAY_ELEMENTS(o.data) AS e(usr) ON TRUE 是否等同于 CROSS JOIN LATERAL JSONB_ARRAY_ELEMENTS(o.data) AS e(usr)
    【解决方案2】:

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

    具体可用的功能取决于版本。但是,既然您说您考虑切换,假设更新的版本应该是公平的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-16
      • 1970-01-01
      • 2020-12-09
      • 1970-01-01
      • 1970-01-01
      • 2015-04-13
      • 2019-02-17
      • 1970-01-01
      相关资源
      最近更新 更多