次のようなクエリがあります。
SELECT a.id, a.name, json_agg(b.*) as "item"
FROM a
JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;
b
の列を選択して、JSONオブジェクトにb.item_id
を含めないようにするにはどうすればよいですか?
ROW
について読みましたが、次のようなJSONオブジェクトが返されます。
{"f1": "Foo", "f2": "Bar"}
適切な列キーと一致するようにフェッチされたら、JSONオブジェクトを再マップする必要があります。それを避けて、元の列名を保持したいと思います。
残念ながら、SQL構文には「この1つの列を除くすべての列」と言う規定はありません。残りの列のリストを row-type expression で綴ることで目標を達成できます。
_SELECT a.id, a.name
, json_agg((b.col1, b.col2, b.col3)) AS item
FROM a
JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;
_
これは、より明示的な形式の略です:ROW
_(b.col1, b.col2, b.col3)
_。
ただし、列名は、行タイプの式では保持されません。このようにして、JSONオブジェクトで一般的なキー名を取得します。元の列名を保持する3つのオプションが表示されます。
既知の(登録された)行タイプにキャストします。タイプは、既存のすべてのテーブルまたはビューに対して、または明示的な_CREATE TYPE
_ステートメントで登録されます。一時的なテーブルをアドホックソリューションに使用する場合があります(セッションの期間中有効)。
_CREATE TEMP TABLE x (col1 int, col2 text, col3 date); -- use adequate data types!
SELECT a.id, a.name
, json_agg((b.col1, b.col2, b.col3)::x) AS item
FROM a
JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;
_
副選択を使用して派生テーブルを作成し、テーブル全体を参照します。これには列名も含まれます。より冗長ですが、登録された型は必要ありません。
_SELECT a.id, a.name
, json_agg((SELECT x FROM (SELECT b.col1, b.col2, b.col3) AS x)) AS item
FROM a
JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;
_
json_build_object()
Postgres 9.4以降_SELECT a.id, a.name
, json_agg(json_build_object('col1', b.col1, 'col2', b.col2, 'col3', b.col3)) AS item
FROM a
JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;
_
関連:
jsonb
についても同様であり、それぞれの関数 jsonb_agg()
および jsonb_build_object()
。
Postgres 9.5以降の場合は、 a_horseの回答 も参照してください。新しい構文の変形:Postgresが マイナス演算子_-
_ for jsonb
言う "この1つのキーを除くすべてのキー" 。
Postgres 10 "except multiple keys"が同じ演算子を使用して実装されているため、 _text[]
_第2オペランド-mltのようにコメント。
9.6以降では、単純に_-
_を使用してJSONBからキーを削除できます。
_SELECT a.id, a.name, jsonb_agg(to_jsonb(b) - 'item_id') as "item"
FROM a
JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;
_
to_jsonb(b)
は行全体を変換し、_- 'item_id'
_は名前_item_id
_のキーを削除し、その結果が集計されます。
サブクエリを使用して、グループ化なしで実際にそれを行うことができます
SELECT
a.id, a.name,
(
SELECT json_agg(item)
FROM (
SELECT b.c1 AS x, b.c2 AS y
FROM b WHERE b.item_id = a.id
) item
) AS items
FROM a;
戻り値
{
id: 1,
name: "thing one",
items:[
{ x: "child1", y: "child1 col2"},
{ x: "child2", y: "child2 col2"}
]
}
このJohn Attenの記事 は非常に興味深いものであり、詳細が記載されています
JSONを作成してから集約するのが最適であることがわかりました。例えば.
with base as (
select a, b, ('{"ecks":"' || to_json(x) || '","wai":"' || to_json(y) || '","zee":"' || to_json(z) || '"}"')::json c
) select (a, b, array_to_json(array_agg(c)) as c)
CTEが気に入らない場合(または使用するためにパフォーマンスの問題が発生する場合)は、サブクエリとして実行できます。
また、これを頻繁に実行する場合は、キーと値のペアをラップする関数を作成して、コードをすっきり見せるとよいでしょう。関数(たとえば)'ecks', 'x'
を渡すと、"ecks": "x"
が返されます。
1ビットを除くすべての列を選択する方法はまだありませんが、json_agg(to_json(b.col_1, b.col_2, b.col_3 ...))
を使用して、{"col_1":"col_1 value", ...}
という形式のjsonのjson配列を取得できます。
したがって、クエリは次のようになります。
SELECT a.id, a.name, json_agg(to_json(b.col_1,b.col_2,b.col_3...)) as item
FROM a
JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;
行を次のように返します。
id, name, item
8, the_name, [{"col_1":"value_1","col_2":"value_2","col_3":"value_3"...}, {"col_1":"value_1.2","col_2":"value_2.2","col_3":"value_3.2"...},...]
9, the_next_name, [{"col_1":"value_1.3","col_2":"value_2.3","col_3":"value_3.3"...}, {"col_1":"value_1.4","col_2":"value_2.4","col_3":"value_3.4"...},...]
...
(私は今Postgres 9.5.3を使用していますが、このサポートがいつ追加されたかは100%わかりません。)
このようにjson_build_object
を使用できます
SELECT
a.id,
a.name,
json_agg(json_build_object('col1', b.col1, 'col2', b.col2) AS item
FROM a
JOIN b ON b.item_id = a.id
GROUP BY a.id, a.name;