我想让 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 函数和运算符?
顺便说一句,我不确定我的想法。这是一个通常的设计决定吗?我的想法是,当然,我可以获取上述 3 个查询中第一个查询的结果(例如),并在将其提供给客户端之前在应用程序中对其进行轻微操作,但如果 PostgreSQL 可以直接创建最终的 JSON 对象,它会更简单,因为我仍然没有在我的应用程序中包含对任何 JSON 库的任何依赖。
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 对象数组,每行都转换为单个对象。结果如下所示:
该json_agg函数开箱即用地产生这个结果。它会自动计算出如何将其输入转换为 JSON 并将其聚合到一个数组中。
json_agg
没有jsonb(在 9.4 中引入)版本的json_agg. 您可以将行聚合到一个数组中,然后将它们转换:
jsonb
SELECT to_jsonb(array_agg(t)) FROM t
或json_agg与演员组合:
SELECT json_agg(t)::jsonb FROM t
我的测试表明,首先将它们聚合到一个数组中要快一些。我怀疑这是因为演员必须解析整个 JSON 结果。
9.2 没有json_aggorto_json功能,所以你需要使用旧的array_to_json:
to_json
array_to_json
SELECT array_to_json(array_agg(t)) FROM t
您可以选择在查询中包含row_to_json调用:
row_to_json
SELECT array_to_json(array_agg(row_to_json(t))) FROM t
这会将每一行转换为 JSON 对象,将 JSON 对象聚合为一个数组,然后将该数组转换为 JSON 数组。
我无法辨别两者之间的任何显着性能差异。
本节介绍如何生成 JSON 对象,每个键是表中的一列,每个值是该列值的数组。结果如下所示:
我们可以利用该json_build_object功能:
json_build_object
您还可以聚合列,创建单行,然后将其转换为对象:
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会稍微快一些。
array_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.
to_jsonb
或者您可以使用显式强制转换:
SELECT json_build_object( 'a', json_agg(t.a), 'b', json_agg(t.b) )::jsonb FROM t
根据我的测试,该to_jsonb版本可以避免演员阵容并且速度更快;再次,我怀疑这是由于解析和验证结果的开销。
该json_build_object函数是 9.5 的新功能,因此您必须在以前的版本中聚合并转换为对象:
取决于你想要json还是jsonb.
json
(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 函数 中查找 JSON 函数的文档。
json_agg位于聚合函数页面上。
如果性能很重要,请确保根据自己的模式和数据对查询进行基准测试,而不是相信我的测试。
它是否是一个好的设计实际上取决于您的特定应用程序。在可维护性方面,我没有看到任何特别的问题。它简化了您的应用程序代码,并意味着在应用程序的该部分中需要维护的内容更少。如果 PG 可以为您提供开箱即用的准确结果,那么我能想到的不使用它的唯一原因就是性能方面的考虑。不要重新发明轮子。
NULL聚合函数通常在对零行进行操作时回馈。如果这是一种可能性,您可能希望使用COALESCE来避免它们。几个例子:
NULL
COALESCE
SELECT COALESCE(json_agg(t), '[]'::json) FROM t
SELECT to_jsonb(COALESCE(array_agg(t), ARRAY[]::t[])) FROM t