PostgreSQL将结果集返回为JSON数组?

时间:2014-06-03 02:40:03

标签: json postgresql

我想让PostgreSQL将查询结果作为一个JSON数组返回。给定

create table t (a int primary key, b text);

insert into t values (1, 'value1');
insert into t values (2, 'value2');
insert into t values (3, 'value3');

我想要类似于

的东西
[{"a":1,"b":"value1"},{"a":2,"b":"value2"},{"a":3,"b":"value3"}]

{"a":[1,2,3], "b":["value1","value2","value3"]}

(实际上知道两者会更有用)。我尝试了一些像

这样的东西
select row_to_json(row) from (select * from t) row;
select array_agg(row) from (select * from t) row;
select array_to_string(array_agg(row), '') from (select * from t) row;

我觉得我很亲密,但不是真的。我应该查看除9.15. JSON Functions and Operators以外的其他文档吗?

顺便说一句,我不确定我的想法。这是一个通常的设计决定吗?我的想法是,我当然可以获取上述3个查询中的第一个的结果(例如)并在将其提供给客户端之前在应用程序中稍微操作它,但是如果PostgreSQL可以直接创建最终的JSON对象,它会更简单,因为我还没有在我的应用程序中包含对任何JSON库的依赖。

2 个答案:

答案 0 :(得分:168)

TL; DR

SELECT json_agg(t) FROM t

表示JSON对象数组,

SELECT
    json_build_object(
        'a', json_agg(t.a),
        'b', json_agg(t.b)
    )
FROM t

表示数组的JSON对象。

对象列表

本节介绍如何生成JSON对象数组,每行都转换为单个对象。结果如下:

[{"a":1,"b":"value1"},{"a":2,"b":"value2"},{"a":3,"b":"value3"}]

9.3及以上

json_agg函数可立即生成此结果。它会自动计算出如何将其输入转换为JSON并将其聚合为数组。

SELECT json_agg(t) FROM t

jsonb版本中没有json_agg(在9.4中引入)。您可以将行聚合到一个数组中然后转换它们:

SELECT to_jsonb(array_agg(t)) FROM t

或将json_agg与演员表合并:

SELECT json_agg(t)::jsonb FROM t

我的测试表明,首先将它们聚合到一个数组中要快一点。我怀疑这是因为演员必须解析整个JSON结果。

9.2

9.2没有json_aggto_json函数,因此您需要使用较旧的array_to_json

SELECT array_to_json(array_agg(t)) FROM t

您可以选择在查询中添加row_to_json来电:

SELECT array_to_json(array_agg(row_to_json(t))) FROM t

这会将每一行转换为JSON对象,将JSON对象聚合为一个数组,然后将该数组转换为JSON数组。

我无法辨别两者之间的任何显着性能差异。

列表对象

本节介绍如何生成JSON对象,每个键都是表中的一列,每个值都是列值的数组。它的结果如下:

{"a":[1,2,3], "b":["value1","value2","value3"]}

9.5及以上

我们可以利用json_build_object功能:

SELECT
    json_build_object(
        'a', json_agg(t.a),
        'b', json_agg(t.b)
    )
FROM t

您还可以聚合列,创建单个行,然后将其转换为对象:

SELECT to_json(r)
FROM (
    SELECT
        json_agg(t.a) AS a,
        json_agg(t.b) AS b
    FROM t
) r

请注意,绝对需要对数组进行别名以确保对象具有所需的名称。

哪个更清楚是一个意见问题。如果使用json_build_object函数,我强烈建议在一行上放置一个键/值对以提高可读性。

您也可以使用array_agg代替json_agg,但我的测试表明json_agg稍快一些。

jsonb功能没有json_build_object版本。您可以聚合成一行并转换:

SELECT to_jsonb(r)
FROM (
    SELECT
        array_agg(t.a) AS a,
        array_agg(t.b) AS b
    FROM t
) r

与此类结果的其他查询不同,array_agg在使用to_jsonb时似乎要快一些。我怀疑这是由于开销解析和验证json_agg的JSON结果。

或者您可以使用显式演员:

SELECT
    json_build_object(
        'a', json_agg(t.a),
        'b', json_agg(t.b)
    )::jsonb
FROM t

根据我的测试,to_jsonb版本允许你避免演员表并且速度更快;再次,我怀疑这是由于解析和验证结果的开销。

9.4和9.3

json_build_object函数是9.5的新功能,因此您必须聚合并转换为以前版本中的对象:

SELECT to_json(r)
FROM (
    SELECT
        json_agg(t.a) AS a,
        json_agg(t.b) AS b
    FROM t
) r

SELECT to_jsonb(r)
FROM (
    SELECT
        array_agg(t.a) AS a,
        array_agg(t.b) AS b
    FROM t
) r

取决于您是想要json还是jsonb

(9.3没有jsonb。)

9.2

在9.2中,甚至不存在to_json。您必须使用row_to_json

SELECT row_to_json(r)
FROM (
    SELECT
        array_agg(t.a) AS a,
        array_agg(t.b) AS b
    FROM t
) r

文档

JSON functions中找到JSON函数的文档。

json_agg位于aggregate functions页面上。

设计

如果性能很重要,请确保根据自己的架构和数据对查询进行基准测试,而不是相信我的测试。

它是一个好的设计还是不是真的取决于你的具体应用。在可维护性方面,我没有看到任何特定问题。它简化了您的应用程序代码,意味着在该部分应用程序中维护的次数更少。如果PG能够为您提供开箱即用的结果,那么我认为不使用它的唯一原因就是性能方面的考虑。不要重新发明轮子和所有。

空值

聚合函数通常在零行操作时返回NULL。如果有可能,您可能希望使用COALESCE来避免它们。几个例子:

SELECT COALESCE(json_agg(t), '[]'::json) FROM t

或者

SELECT to_jsonb(COALESCE(array_agg(t), ARRAY[]::t[])) FROM t

Hannes Landeholm

pointing this out

答案 1 :(得分:11)

此外,如果您希望从表中选择字段并将其聚合为数组。

SELECT json_agg(json_build_object('data_a',a,
                                  'data_b',b,
))  from t;

结果将会到来。

 [{'data_a':1,'data_b':'value1'}
  {'data_a':2,'data_b':'value2'}]