1

で簡単なテーブルを定義しました

create table resources (id serial primary key, fields jsonb);

また、キー (大規模なセットから取得) と 1 から 100 までの値を持つデータが含まれています。

   id   |    fields                                                                                                 
--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
      1 | {"tex": 23, "blair": 46, "cubic": 50, "raider": 57, "retard": 53, "hoariest": 78, "suturing": 25, "apostolic": 22, "unloosing": 37, "flagellated": 85}
      2 | {"egoist": 75, "poshest": 0, "annually": 19, "baptists": 29, "bicepses": 10, "eugenics": 9, "idolizes": 8, "spengler": 60, "scuppering": 13, "cliffhangers": 37}
      3 | {"entails": 27, "hideout": 22, "horsing": 98, "abortions": 88, "microsoft": 37, "spectrums": 26, "dilettante": 52, "ringmaster": 84, "floweriness": 72, "vivekananda": 24}
      4 | {"wraps": 6, "polled": 68, "coccyges": 63, "internes": 93, "unburden": 61, "aggregate": 76, "cavernous": 98, "stylizing": 65, "vamoosing": 35, "unoriginal": 40}
      5 | {"villon": 95, "monthly": 68, "puccini": 30, "samsung": 81, "branched": 33, "congeals": 6, "shriller": 47, "terracing": 27, "patriarchal": 86, "compassionately": 94}

(特定のキーに関連付けられた) 値がベンチマーク値より大きいエントリを検索したいと思います。たとえば、次の方法でこれを実現できます。

with exploded as (
    select id, (jsonb_each_text(fields)).*
    from resources)
select distinct id
    from exploded
    where key='polled' and value::integer>50;

...しかしもちろん、これはインデックスを使用せず、テーブルスキャンに頼っています。私はあるのだろうか:

  1. "polled" > 50 のリソースをクエリするより効率的な方法
  2. この種のクエリをサポートするインデックスを構築する方法
4

1 に答える 1

7

どのような種類のINDEX使用が期待されているかを指定しておらず、その定義も提供していません。

フィールドの典型的なINDEXものはGINのものですが、特定のケースでは、キーに含まれるいくつかの値を実際に比較する必要があります。jsonbpolled

たぶん、を含む特定のINDEX(1 つではありませんがGIN!)使用できる可能性がありますが、整数値を取得するには少なくとも double 型キャストが必要であり、実際にカスタム関数を使用する必要があるため、かなり面倒になる可能性があります。ステートメントで型キャストを実行します。IMMUTABLECREATE INDEX

特定のケースのみを解決する複雑なルートを取る前に (別のfieldsキーとの別の比較が必要な場合はどうすればよいでしょうか?)、PostgreSQL 9.4 の新しいLATERAL機能とjsonb処理関数を利用して、現在のクエリを最適化することができます。その結果、現在のクエリよりも最大 8 倍高速に実行されるクエリが得られます。

SELECT r.id 
    FROM resources AS r,
    LATERAL jsonb_to_record(r.fields) AS l(polled integer) 
    WHERE l.polled > 50;



編集 :

実際に値を比較する前に、 a を使用して行数を制限するという私のコメントのアイデアを実践するために簡単なテストを行いました。そのような状況でもGIN INDEXa を実際に使用できることがわかりました。GIN INDEX

INDEX、デフォルトのオペレーター クラスで作成する必要がありますjsonb_ops (軽量でパフォーマンスの高いものではありませんjsonb_path_ops)

CREATE INDEX ON resources USING GIN (fields);

?クエリに存在テストを含めるだけで、インデックスを利用できるようになりました。

SELECT r.id
    FROM resources AS r,
    LATERAL jsonb_to_record(r.fields) AS l(polled integer) 
    WHERE r.fields ? 'polled' AND l.polled > 50;

クエリの実行速度が約3 倍になり ました (最初の CTE バージョンよりも約 20 倍高速です)。最大 100 万行でテストしましたが、パフォーマンスの向上は常に同じです。


予想どおり、行数が重要な役割を果たすことに注意してください。1,000 行未満の場合、インデックスはまったく役に立たず、クエリ プランナーはおそらくそれを使用しません。

jsonb_opsまた、実際のデータに比べてインデックスが巨大になる可能性があることも忘れないでください。あなたのようなデータ サンプルでは、​​1K から 1M 行の範囲で、インデックス自体がテーブル内の実際のデータよりも約170%大きくなります。自分で確認してください。

SELECT pg_size_pretty(pg_total_relation_size('resources')) AS whole_table, 
       pg_size_pretty(pg_relation_size('resources')) AS data_only, 
       pg_size_pretty(pg_relation_size('resources_fields_idx')) AS gin_index_only;

参考までに、データ サンプルのように約 300K 行あると、テーブルは約 250MB になり、90MB のデータと 160MB のインデックスで構成されます。個人的には、インデックスのない単純なものに固執します (実際にそうしています) 。LATERAL JOIN

于 2015-05-08T11:08:06.740 に答える