【问题标题】:How to add N entries to json{b}_build_object where N is number of rows in table?如何向 json{b}_build_object 添加 N 个条目,其中 N 是表中的行数?
【发布时间】:2021-10-28 08:08:26
【问题描述】:

鉴于此表设置:

create table accounts (
    id char(4) primary key,
    first_name varchar not null
);

create table roles (
    account_id char(4) references accounts not null,
    role_type varchar not null,
    role varchar not null,

    primary key (account_id, role_type)
);

和初始帐户插入:

insert into accounts (id, first_name) values ('abcd', 'Bob');

我想获取某人的所有帐户信息,以及他们作为键值对所拥有的角色。对这种一对多关系使用连接会在包含角色的每一行中复制帐户信息,因此我想创建一个 JSON 对象。使用这个查询:

select
    first_name,
    coalesce(
        (select jsonb_build_object(role_type, role) from roles where account_id = id), 
        '{}'::jsonb
    ) as roles
from accounts where id = 'abcd';

我得到了这个预期的结果:

 first_name | roles 
------------+-------
 Bob        | {}
(1 row)

添加第一个角色后:

insert into roles (account_id, role_type, role) values ('abcd', 'my_role_type', 'my_role');

我得到另一个预期结果:

 first_name |            roles            
------------+-----------------------------
 Bob        | {"my_role_type": "my_role"}
(1 row)

但添加第二个角色后:

insert into roles (account_id, role_type, role) values ('abcd', 'my_other_role_type', 'my_other_role');

我明白了:

ERROR:  more than one row returned by a subquery used as an expression

如何用

替换此错误
 first_name |            roles            
------------+-----------------------------
 Bob        | {"my_role_type": "my_role", "my_other_role_type": "my_other_role"}
(1 row)

?

我正在使用 Postgres v13。

【问题讨论】:

    标签: sql json postgresql


    【解决方案1】:

    您可以将json_objectarray_agg 与group by 一起使用以实现此结果。请参阅下面的工作小提琴示例:

    查询 #1

    select
        a.first_name,
        json_object(
             array_agg(role_type),
             array_agg(role)
        )
    from accounts a
    inner join roles r on r.account_id = a.id
    where a.id = 'abcd'
    group by a.first_name;
    
    first_name json_object
    Bob {"my_role_type":"my_role","my_other_role_type":"my_other_role"}

    View on DB Fiddle

    编辑 1:

    以下修改使用左连接和 case 表达式为包含空值的结果提供替代方案:

    select
        a.first_name,
        CASE 
            WHEN COUNT(role_type)=0 THEN '{}'::json
            ELSE
                json_object(
                    array_agg(role_type),
                    array_agg(role)
                )
        END as role
    from accounts a
    left join roles r on r.account_id = a.id
    group by a.first_name;
    

    View on DB Fiddle

    让我知道这是否适合你。

    【讨论】:

    • 感谢ggordon,到目前为止效果很好。但是因为a.id 是主键,r.account_id = a.id 将只剩下一个帐户。既然加入的时候只有一个账号,那我为什么要留着群呢? Postgres 说每个选择列都必须出现在其中,但每个过滤器只有一个帐户。
    • group by 在这个例子中促进了array_agg 函数,它是一个聚合函数。您可以删除 group by,但是 firstname 或其他非聚合列也需要在聚合中。我在这个example中使用了max函数来演示。
    猜你喜欢
    • 1970-01-01
    • 2022-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-08
    • 1970-01-01
    • 2021-01-22
    • 2015-09-02
    相关资源
    最近更新 更多