13

という名前の列teamsを含むテーブルで PostgreSQL 9.4 を使用しています。Playersを持つすべてのチームと、そのプレーヤーの配列を取得できるクエリを探しています。jsonbjson347

テーブルには、次のjsonデータを含む 2 つの行が含まれています。

最初の行:

{
    "id": 1,
    "name": "foobar",
    "members": {
        "coach": {
            "id": 1,
            "name": "A dude"
        },
        "players": [
            {
                "id": 2,
                "name": "B dude"
            },
            {
                "id": 3,
                "name": "C dude"
            },
            {
                "id": 4,
                "name": "D dude"
            },
            {
                "id": 6,
                "name": "F dude"
            },
            {
                "id": 7,
                "name": "G dude"
            }
        ]
    }
}

2 行目:

{
    "id": 2,
    "name": "bazbar",
    "members": {
        "coach": {
            "id": 11,
            "name": "A dude"
        },
        "players": [
            {
                "id": 3,
                "name": "C dude"
            },
            {
                "id": 5,
                "name": "E dude"
            },
            {
                "id": 6,
                "name": "F dude"
            },
            {
                "id": 7,
                "name": "G dude"
            },
            {
                "id": 8,
                "name": "H dude"
            }
        ]
    }
}

必要なチームのリストを取得するには、どのようなクエリを作成する必要がありますか? メンバー プレーヤーから配列を作成して比較するクエリを試しましたjsonb_array_elements(json -> 'members' -> 'players')->'id'が、すべてではなく、比較されたプレーヤー ID のいずれかがチームで利用可能であるという結果しか得られませんでした。

4

2 に答える 2

16

あなたは一度に 2 つの重要なタスクに直面しています。私は興味をそそられます。

  • jsonb複雑な入れ子構造を持つプロセス。
  • ドキュメント タイプに対して関係除算クエリに相当するものを実行します。

まず、行タイプを に登録しjsonb_populate_recordset()ます。を使用してタイプを永続的に作成するか、CREATE TYPEアドホックに使用する一時テーブルを作成できます (セッションの終了時に自動的に削除されます)。

CREATE TEMP TABLE foo(id int);  -- just "id", we don't need "name"

のみが必要なidので、 は含めないでくださいnameドキュメントごと:

ターゲット行タイプに表示されない JSON フィールドは、出力から省略されます

クエリ

SELECT t.json->>'id' AS team_id, p.players
FROM   teams t
     , LATERAL (SELECT ARRAY (
         SELECT * FROM jsonb_populate_recordset(null::foo, t.json#>'{members,players}')
         )
       ) AS p(players)
WHERE p.players @> '{3,4,7}';

Postgres 9.3のSQL Fiddlejson (pg 9.4 はまだ利用できません)。

説明

  • プレイヤー レコードを含む JSON 配列を抽出します。

    t.json#>'{members,players}'
    
  • これらから、with だけで行のネストを解除しますid

    jsonb_populate_recordset(null::foo, t.json#>'{members,players}')
    

    ...そしてすぐにそれらをPostgres配列に集約するため、ベーステーブルに行ごとに1行を保持します:

    SELECT ARRAY ( ... )
    
  • これはすべて横結合で行われます。

    , LATERAL (SELECT ... ) AS p(players)
    
  • "contains"配列演算子@>を使用して、結果の配列をすぐにフィルター処理して、探しているものだけを保持します。

    WHERE p.players @> '{3,4,7}'
    

出来上がり。

このクエリを大きなテーブルで何度も実行する場合は、IMMUTABLE上記のような配列を抽出する偽の関数を作成し、この関数に基づいて機能的なGIN インデックスを作成して、これを超高速にすることができます。
関数が基になる行の型、つまりカタログ参照に依存し、それが変更されると変更されるため、「偽」。(変更されないことを確認してください。) これと同様:

余談ですが、列名
のような型名を使用しないでくださいjson(たとえ許可されていても)、トリッキーな構文エラーや紛らわしいエラー メッセージを招きます。

于 2015-03-18T05:07:49.883 に答える