web-dev-qa-db-ja.com

json_agg内の列を選択します

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

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オブジェクトを再マップする必要があります。それを避けて、元の列名を保持したいと思います。

24
Yanick Rochon

残念ながら、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つのオプションが表示されます。

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が マイナス演算子_-_ for jsonb 言う "この1つのキーを除くすべてのキー"
Postgres 10 "except multiple keys"が同じ演算子を使用して実装されているため、 _text[]_第2オペランド-mltのようにコメント。

58

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_のキーを削除し、その結果が集計されます。

21

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

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の記事 は非常に興味深いものであり、詳細が記載されています

8
redben

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"が返されます。

2
MikeM

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%わかりません。)

1
David K

このように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;
1
Tan Duong