我想让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库的依赖。
答案 0 :(得分:168)
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"}]
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没有json_agg
或to_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"]}
我们可以利用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
版本允许你避免演员表并且速度更快;再次,我怀疑这是由于解析和验证结果的开销。
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中,甚至不存在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
的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'}]