将array_agg/array_to_json应用于具有已修改列的查询

hs0*_*hs0 12 postgresql json

我将array_to_json与array_agg结合使用,将PostgreSQL中的某些结果格式化为JSON.这适用于我想要返回查询的默认值的查询(所有列,未修改).但我很难理解如何使用array_agg为查询创建一个JSON对象,我想在其中修改一些输出.

这是一个例子.假设我们有以下内容:

CREATE TABLE temp_user ( 
   user_id  serial PRIMARY KEY,
   real_name text
);
CREATE TABLE temp_user_ip (
   user_id  integer,
   ip_address text
);
INSERT INTO temp_user (user_id, real_name) VALUES (1, 'Elise'),  (2, 'John'), (3, NULL);
INSERT INTO temp_user_ip (user_id, ip_address) VALUES (1, '10.0.0.4'),  (2, '10.0.0.7'), (3, '10.0.0.9');
Run Code Online (Sandbox Code Playgroud)

以下查询工作正常:

# SELECT array_to_json(array_agg(temp_user)) as users from temp_user;
                                            users                                                
-----------------------------------------------------------------------------------------------------
 [{"user_id":1,"real_name":"Elise"},{"user_id":2,"real_name":"John"},{"user_id":3,"real_name":null}]
Run Code Online (Sandbox Code Playgroud)

但是,让我们说我不喜欢用户3出现的空值.我宁愿看到字符串"用户从$ ip登录".

我可以做这个:

# SELECT user_id, (CASE WHEN real_name IS NULL THEN (select 'User logged in from ' || ip_address FROM temp_user_ip WHERE user_id = temp_user.user_id) ELSE real_name END) as name from temp_user;
Run Code Online (Sandbox Code Playgroud)

我得到以下结果:

 user_id |             name             
---------+------------------------------
       1 | Elise
       2 | John
       3 | User logged in from 10.0.0.9
Run Code Online (Sandbox Code Playgroud)

哪个好.但我无法弄清楚如何像第一个例子那样将这些数据操作为JSON格式.

所需的输出当然如下:

[{"user_id":1,"name":"Elise"},{"user_id":2,"name":"John"},{"user_id":3,"name":"User logged in from 10.0.0.9"}]
Run Code Online (Sandbox Code Playgroud)

以下不起作用:

# select array_to_json(array_agg ( (SELECT user_id, (CASE WHEN real_name IS NULL THEN (select 'User logged in from ' || ip_address FROM temp_user_ip WHERE user_id = temp_user.user_id) ELSE real_name END) as name from temp_user)));
ERROR:  subquery must return only one column
Run Code Online (Sandbox Code Playgroud)

我无法想出任何方法将数据转换为array_agg接受的格式.我甚至尝试创建一个与temp_user格式匹配的自定义类型,并尝试对类型构造函数进行array_agg调用,这会返回相同的错误.这个错误对我没有意义 - 如果子查询是聚合的,那么它返回多个列应该无关紧要.有什么建议?

dez*_*zso 18

您可以将聚合调用与子查询分开,并使用row构造函数生成复合数据:

SELECT 
    array_to_json(array_agg(row(t.*))) AS users 
FROM 
    (
        SELECT user_id, 
            CASE 
                WHEN real_name IS NULL 
                THEN (
                    SELECT 'User logged in from ' || ip_address 
                    FROM temp_user_ip 
                    WHERE user_id = temp_user.user_id
                ) ELSE real_name 
            END AS name 
        FROM temp_user
    ) t
;
Run Code Online (Sandbox Code Playgroud)

你也可以在SQLFiddle上查看这个.

  • 通过简单的`array_agg(t)`替换`array_agg(row(t.*))`可以简化查询.这样,您还可以保留子查询中的列名称. (8认同)

Fra*_*nry 5

这里只是一个更新,postgres现在有一个json_agg可以在leiu中使用array_to_json(array_agg( ... ))但在某些情况下实际上表现更好的函数,请看这里:postgres中的Array_agg有选择地引用

文档:https://www.postgresql.org/docs/9.5/static/functions-aggregate.html

这是修改后的查询:

SELECT 
    json_agg(row(t.*)) AS users 
FROM 
    (
        SELECT user_id, 
            CASE 
                WHEN real_name IS NULL 
                THEN (
                    SELECT 'User logged in from ' || ip_address 
                    FROM temp_user_ip 
                    WHERE user_id = temp_user.user_id
                ) ELSE real_name 
            END AS name 
        FROM temp_user
    ) t
;
Run Code Online (Sandbox Code Playgroud)