PostgreSQL返回结果集为JSON数组?

5kgi1eie  于 2023-02-08  发布在  PostgreSQL
关注(0)|答案(2)|浏览(161)

我想让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之外的其他文档吗?
顺便说一下,我不确定我的想法。这是一个通常的设计决策吗?我的想法是,我当然可以采取的结果(例如),并在将其提供给客户端之前在应用程序中对其进行轻微操作,但如果PostgreSQL可以直接创建最终的JSON对象,则会更简单,因为我还没有在应用程序中包含对任何JSON库的依赖。

kt06eoxx

kt06eoxx1#

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

json_agg没有jsonb(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稍微快一些。
json_build_object函数没有jsonb版本。可以聚合到单行中并转换:

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函数中查找JSON函数的文档。
json_agg位于聚合函数页。
设计
如果性能很重要,请确保您的查询是针对您自己的模式和数据进行基准测试的,而不是相信我的测试。
它是否是一个好的设计,实际上取决于你的具体应用。在可维护性方面,我看不出有什么特别的问题。它简化了你的应用代码,意味着在应用的这一部分需要维护的东西更少。如果PG能给你开箱即用的结果,我能想到的不使用它的唯一原因就是性能方面的考虑。不要重新发明轮子之类的东西。

空值

聚合函数在操作零行时通常返回NULL。如果可能,您可能希望使用COALESCE来避免这种情况。下面是几个示例:

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

或者

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

感谢Hannes Landeholm指出这一点

rvpgvaaj

rvpgvaaj2#

此外,如果要从表中选择字段并将其聚合为数组:

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'}]

相关问题