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

Posted

技术标签:

【中文标题】PostgreSQL 将结果集返回为 JSON 数组?【英文标题】:PostgreSQL return result set as JSON array? 【发布时间】:2014-07-23 06:21:32 【问题描述】:

我想让 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 库的任何依赖。

【问题讨论】:

PG 9.4,现在在 beta 1 版本中可用,改进了对 JSON 的支持,包括二进制 I/O。如果你在开发机器上,你可能想检查一下。 @Patrick:谢谢,看起来 json_object() 是 9.4 中的一个新函数,我会尝试类似 SELECT json_object(array_agg(ta),array_agg(tb)) FROM t我有它 【参考方案1】:

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(9.4 中引入)版本的json_agg。您可以将行聚合成一个数组,然后将它们转换:

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

【讨论】:

感谢您的回答。您启发了我找到第二个问题 SELECT row_to_json(row(array_agg(ta),array_agg(tb))) FROM t 的答案,尽管结果标签是“f1”和“f2”而不是 a 和 b。 @engineerX 我已经扩展了我的答案。 当内部选择(来自 t)返回零行时,在某些情况下返回 NULL 而不是空的 JSON 数组可能是不可取的。这是由于聚合函数在选择没有行时总是返回 NULL 并且可以通过合并解决:array_to_json(coalesce(array_agg(t), array[]::record[]))。 您可以使用to_json 代替row_to_jsonarray_to_json “没有jsonb(9.4引入)版本的json_agg”:相信现在有jsonb_agg函数【参考方案2】:

另外,如果您想从表中选择字段并聚合为数组。

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

【讨论】:

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

以上是关于PostgreSQL 将结果集返回为 JSON 数组?的主要内容,如果未能解决你的问题,请参考以下文章

postgresql 函数返回结果集(zz)

来自 PostgreSQL 函数的多个 JDBC 结果集

postgresql 中的函数调用不返回结果集

PostgreSQl 函数返回多个动态结果集

postgresql 查询中的大结果集

PostgreSQL & JDBC“查询返回了多个结果集”