如何使用GROUP BY子句为json聚合结果设置正确的属性名称?

Prz*_*mek 11 postgresql json types aggregate-functions postgresql-9.3

我有一个temp像这样定义的表:

id |  name  |  body  | group_id
-------------------------------
1  | test_1 | body_1 | 1
2  | test_2 | body_2 | 1
3  | test_3 | body_3 | 2
4  | test_4 | body_4 | 2
Run Code Online (Sandbox Code Playgroud)

我想生成一个按group_idjson 分组并汇总的结果.但是,查询如下:

SELECT group_id, json_agg(ROW(id, name, body)) FROM temp
GROUP BY group_id;
Run Code Online (Sandbox Code Playgroud)

产生这个结果:

1;[{"f1":1,"f2":"test_1","f3":"body_1"}, 
   {"f1":2,"f2":"test_2","f3":"body_2"}]
2;[{"f1":3,"f2":"test_3","f3":"body_3"}, 
   {"f1":4,"f2":"test_4","f3":"body_4"}]
Run Code Online (Sandbox Code Playgroud)

在JSON对象的属性被命名为f1,f2,f3而不是id,name,body按要求.我知道可以通过使用子查询或公用表表达式来正确地对它们进行别名,例如:

SELECT json_agg(r.*) FROM (
  SELECT id, name, body FROM temp
) r;
Run Code Online (Sandbox Code Playgroud)

产生这个结果:

[{"id":1,"name":"test_1","body":"body_1"}, 
 {"id":2,"name":"test_2","body":"body_2"}, 
 {"id":3,"name":"test_3","body":"body_3"}, 
 {"id":4,"name":"test_4","body":"body_4"}]
Run Code Online (Sandbox Code Playgroud)

但老实说,我没有看到如何将它与聚合结合使用.我错过了什么?

小智 30

在Postgres 9.4中,您可以使用json_build_object().

对于您的示例,它的工作方式如下:

SELECT group_id, 
       json_agg(json_build_object('id', id, 'name', name, 'body', body)) 
FROM temp
GROUP BY group_id;
Run Code Online (Sandbox Code Playgroud)

这是一种更友好的方式,Postgres爱我们:3


Cra*_*ger 17

你不需要临时表或类型,但它并不漂亮.

SELECT json_agg(row_to_json( (SELECT r FROM (SELECT id, name, body) r) )) 
FROM t
GROUP BY group_id;
Run Code Online (Sandbox Code Playgroud)

在这里,我们使用两个子查询 - 首先,构造仅包含三个所需列的结果集,然后使用外部子查询将其作为复合行类型.

它仍然会表现良好.


为了用更简单的语法来完成,PostgreSQL需要让你为匿名行类型设置别名,如下面的(无效)语法:

SELECT json_agg(row_to_json( ROW(id, name, body) AS (id, name, body) )) 
FROM t
GROUP BY group_id;
Run Code Online (Sandbox Code Playgroud)

或者我们需要一个带有row_to_json列别名的变体,比如(再次无效):

SELECT json_agg(row_to_json( ROW(id, name, body), ARRAY['id', 'name', 'body'])) 
FROM t
GROUP BY group_id;
Run Code Online (Sandbox Code Playgroud)

其中两个都很好,但目前不支持.


Clo*_*eto 5

建立在@克雷格的答案,使其更加优雅,这里的复合行类型是建立在from列表

select json_agg(row_to_json(s))
from
    t
    cross join lateral 
    (select id, name, body) s
group by group_id;
                                       json_agg                                       
--------------------------------------------------------------------------------------
 [{"id":1,"name":"test_1","body":"body_1"}, {"id":2,"name":"test_2","body":"body_2"}]
 [{"id":3,"name":"test_3","body":"body_3"}, {"id":4,"name":"test_4","body":"body_4"}]
Run Code Online (Sandbox Code Playgroud)