band
テーブルがあり、配列を保持するjson
列があるとします。
id | people
---+-------------
1 | ['John', 'Thomas']
2 | ['John', 'James']
3 | ['James', 'George']
それぞれの名前が含まれているバンドの数をリストする方法は?
必要な出力:
name | count
-------+------------
John | 2
James | 2
Thomas | 1
George | 1
列people
のデータ型は、json_array_elements(people)
の結果と同様に、json
です。また、データ型json
の等価演算子(_=
_)はありません。したがって、その上で_GROUP BY
_を実行することもできません。もっと:
jsonb
には等値演算子があるため、 回答の「回避策」 はjsonb
にキャストし、同等のjsonb_array_elements()
を使用します。キャストはコストを追加します:
_jsonb_array_elements(people::jsonb)
_
Postgres 9.4以降、 json_array_elements_text(json)
も配列要素をtext
として返します。関連:
そう:
_SELECT p.name, count(*) AS c
FROM band b, json_array_elements_text(b.people) p(name)
GROUP BY p.name;
_
text
オブジェクト(テキスト表現では二重引用符で囲まれています)ではなくjsonb
として名前を取得する方が便利であり、「望ましい出力」は、結果でtext
が必要/必要であることを示します。
text
データの_GROUP BY
_は、jsonb
のデータよりも安価であるため、この代替の「回避策」は2つの理由で高速になるはずです。 (EXPLAIN (ANALYZE, TIMING OFF)
でテストします。)
記録として、 元の答え には何も問題はありませんでした。コンマ(_,
_)は_CROSS JOIN LATERAL
_と同じように「正しい」。標準SQLで以前に定義されていても、それが劣ることはありません。見る:
また、他のRDBMSへの移植性も高くありません。また、jsonb_array_elements()
またはjson_array_elements_text()
は、そもそも他のRDBMSへの移植性がないため、無関係です。短いクエリは_CROSS JOIN LATERAL
_ IMOでは明確になりませんが、最後のビットは私の個人的な意見です。
より明示的なテーブルと列のエイリアスp(name)
とテーブル修飾参照_p.name
_を使用して、重複する可能性のある名前を防ぎました。 name
は一般的なWordであり、基になるテーブルband
の列名としてポップアップ表示される場合があります。その場合、暗黙的に_band.name
_に解決されます。単純なフォームjson_array_elements_text(people) name
は、tableエイリアスのみを付加します。列名は、関数から返されたvalue
のままです。ただし、name
リストで使用すると、value
は単一列SELECT
に解決されます。それは期待どおりに動作します。ただし、真の列名name
(_band.name
_が存在する場合)が最初にバインドされます。与えられた例ではそれは噛みませんが、それはでロードされたフットガン他の場合。
最初に、一般的な「名前」を識別子として使用しないでください。多分それは単純なテストケースのためだけでした。
列people
がプレーンJSON配列以外を保持できる場合、どちらのクエリでも例外がトリガーされます。データの整合性を保証できない場合は、 json_typeof()
で防御することをお勧めします。
_SELECT p.name, count(*) AS c
FROM band b, json_array_elements_text(b.people) p(name)
WHERE json_typeof(b.people) = 'array'
GROUP BY 1; -- optional short syntax since you seem to prefer short syntax
_
違反している行をクエリから除外します。
関連:
@ypercubeᵀᴹコメントに基づいて、私は次のように終わりました:
SELECT name, count(*) as c
FROM band
CROSS JOIN LATERAL jsonb_array_elements(people::jsonb) as name
GROUP BY name;
使用済みjsonb_array_elements
の代わりにunnest
。