json_agg内の列を選択します


21

次のようなクエリがあります:

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;

JSONオブジェクトに含まれbないように列を選択するにはどうすればよいb.item_idですか?

について読みましたがROW、次のようなJSONオブジェクトを返します。

{"f1": "Foo", "f2": "Bar"}

適切な列キーに一致するように取得したら、JSONオブジェクトを再マップする必要があります。それを避け、元の列名を保持したいと思います。

回答:


50

残念ながら、SQL構文には「この1列を除くすべての列」と言う規定はありません。行タイプの式の残りの列のリストを綴ることにより、目標を達成できます。

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つのオプションがあります。

1.登録済みタイプへのキャスト

既知の(登録済み)行タイプにキャストします。タイプは、既存のすべてのテーブルまたはビューに対して、または明示的な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;

2.副選択を使用する

副選択を使用して、派生テーブルを作成し、テーブル全体を参照します。これには列名も含まれます。より冗長ですが、登録されたタイプは必要ありません。

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;

3. 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は追加マイナスオペレータ-のためjsonb言って、「この1つのキーを除くすべてのキーを」
以来、Postgresの10 「複数のキーを除いて、」同じオペレータ撮影を用いて実施されるtext[]第2オペランドとして- MLTは、コメント等が挙げられます。


1
>またはいくつかのキー json(b)-text []は10から使用できることに注意してください
。– mlt

解決策3は私にとって魅力的でした!
ルイスフェルナンド

17

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のキーを削除してから、その結果を集計します。


この新しい機能は、OPが期待していたもののようです。回答へのリンクを追加しました。
アーウィンブランドステッター

json_aggfunction json_agg(record) does not exist
サブセレクト

@fraxture:あなたはPostgres 9.6を使用していない
-a_horse_with_no_name

確かにそれが問題でした。v9.2で列をフィルタリングする方法はありますか?
fraxture

8

サブクエリを使用して、グループ化せずに実際に行うことができます

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"}
  ]
}

ジョン・アッテンのこの記事は本当に興味深く、詳細があります


2

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

1ビットを除くすべての列を選択する方法はまだありませんが、を使用json_agg(to_json(b.col_1, b.col_2, b.col_3 ...))してjsonの形式のjson配列を取得できます{"col_1":"col_1 value", ...}

したがって、クエリは次のようになります。

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%確信できません。)


1

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;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.