JSON配列の要素をネスト解除してGROUP BYする方法は?


8

列が配列を保持bandしているテーブルが与えられた場合json

id | people
---+-------------
1  | ['John', 'Thomas']
2  | ['John', 'James']
3  | ['James', 'George']

それぞれの名前が含まれているバンドの数をリストする方法は?
望ましい出力:

name   | count
-------+------------
John   | 2
James  | 2
Thomas | 1
George | 1

回答:


7

カラムのデータ型がpeopleあるjsonの結果であるように、json_array_elements(people)。また=、データ型には等価演算子()がありません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、結果の最初からの必要性を示します。

GROUP BYon textデータの方がonよりも安いjsonbので、この代替の「回避策」は2つの理由でより速くなるはずです。(EXPLAIN (ANALYZE, TIMING OFF)。でテストします。)

念のため、元の回答に問題はありませんでした。コンマ(,)はと同じように「正しい」CROSS JOIN LATERAL。標準SQLで以前に定義されていても、それが劣ることはありません。見る:

どちらも、それはそれ以上の他のRDBMSに移植し、以来ではないjsonb_array_elements()json_array_elements_text()も無関係だと、そもそも他のRDBMSには移植できません。短いクエリはCROSS JOIN LATERALIMOでは明確になりませんが、最後のビットは私の個人的な意見です。

より明示的なテーブルと列のエイリアスp(name)とテーブル修飾された参照を使用して、p.name重複する可能性のある名前を防ぎました。nameこのような一般的な単語は、基になるテーブルの列名としてポップアップすることもありますband。その場合、暗黙的にに解決されband.nameます。単純なフォームjson_array_elements_text(people) nameは、テーブルエイリアスのみをアタッチしますvalue。関数から返された列名はまだです。ただし、リストで使用すると、name1つの列valueに解決されSELECTます。これは、期待通りの仕事に起こります。ただし、真の列名nameband.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

違反している行をクエリから除外します。

関連:


4

@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


-1

MySQLの誰かのために

SELECT
  JSON_EXTRACT(people, CONCAT('$[', idx, ']')) AS name, count(*) as count
FROM yourtable
JOIN subtable AS indexes
WHERE JSON_EXTRACT(people, CONCAT('$[', idx, '].id')) IS NOT NULL
group by name

サブテーブルのような:カラム:idx、行:0,1,2,3,4,5,6,7,8,9 ...

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