web-dev-qa-db-ja.com

PostgreSQLはJSON配列として結果セットを返しますか?

PostgreSQLにクエリの結果を1つの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ライブラリへの依存関係をまだ含めていないため、それはより簡単です。

90
engineerX

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_aggjsonb(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_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配列に変換されます。

2つのパフォーマンスの大きな違いを識別できませんでした。

リストのオブジェクト

このセクションでは、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関数を使用する場合は、読みやすさを向上させるために、キー/値のペアを1行に配置することを強くお勧めします。

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 functions で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これを指摘

177
jpmc26

また、テーブルからフィールドを選択して、配列として集約する場合もあります。

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'}]
11
Himanshu sharma