web-dev-qa-db-ja.com

多対多の関係にある個別のIDに対する最速のクエリ

私はPostgreSQL 9.4にこのテーブルを持っています:

CREATE TABLE user_operations( 
    id SERIAL PRIMARY KEY, 
    operation_id integer, 
    user_id integer )

テーブルは、すべてのユーザーのセットSの一部のサブセット(それぞれ約~1000-2000要素で構成される)に対応する80000-120000異なる操作で構成されています。

S = {1, 2, 3, ... , 122655}

パラメーター:

work_mem = 128MB
table_size = 880MB

operation_idにもインデックスがあります。

QUESTION:user_idセットの重要な部分についてすべての異なるoperation_idをクエリするための最適な計画は何ですか(20% -60%)のように:

SELECT DISTINCT user_id FROM user_operation WHERE operation_id < 500

テーブルにさらにインデックスを作成することは可能です。現在、クエリの計画は次のとおりです。

HashAggregate  (cost=196173.56..196347.14 rows=17358 width=4) (actual time=1227.408..1359.947 rows=598336 loops=1)
  ->  Bitmap Heap Scan on user_operation  (cost=46392.24..189978.17 rows=2478155 width=4) (actual time=233.163..611.182 rows=2518122 loops=1)
        Recheck Cond: (operation_id < 500)
        ->  Bitmap Index Scan on idx  (cost=0.00..45772.70 rows=2478155 width=0) (actual time=230.432..230.432 rows=2518122 loops=1)
              Index Cond: (operation_id < 500)

このようなクエリプランは、このような状況で本当に最適ですか?つまり、Bitmap Heap Scanを使用することの正確さについてはわかりません。私は関連する記事への参照に感謝します。

5
St.Antario

user_idセット(20%〜60%)の重要な部分について、すべての個別のoperation_idをクエリするための最適な計画はどれですか。

再帰クエリを使用します。

WITH RECURSIVE cte AS (
   (  -- parentheses are required
   SELECT user_id
   FROM   user_operations
   WHERE  operation_id < 500
   ORDER  BY user_id
   LIMIT  1
   )
   UNION ALL
   SELECT u.user_id
   FROM   cte, LATERAL (
      SELECT user_id
      FROM   user_operations
      WHERE  operation_id < 500
      AND    user_id > cte.user_id  -- lateral reference
      ORDER  BY user_id
      LIMIT  1
      ) u
   )
TABLE cte;

(user_id, operation_id)のインデックスとの組み合わせ-列の順序。 2番目の列でフィルター処理するインデックススキャンを期待しています。適度に正確なテーブル統計は重要なので、Postgresは次のuser_idを見つけるためにインデックス内の数行をスキップするだけでよいことを認識しています。一般に、1つのmightは、特にoperation_idの統計ターゲットを増やしたいと考えています。

ALTER TABLE user_operations ALTER operation_id SET STATISTICS 1000;

~1000-2000 different operationsしかないので、これは必要ないかもしれませんが、支払うのは少額です。

詳細:

If述語operation_id < 500が安定している場合(常に同じ)、代わりに(user_id)のみの部分インデックスを作成します。

CREATE INDEX foo ON user_operations (user_id) WHERE operation_id < 500;

その場合、operation_idの統計は、このクエリに関連しなくなります。

述語が安定していない場合でも、可能な条件と値の頻度の全範囲に応じて、最適化する方法がある場合があります。

パフォーマンスは...deliciousである必要があります。

私はSO(詳細な説明付き)に関するこの関連する回答でテクニックを最適化しました:

If別のusersテーブルがあり、すべてのユーザーの大部分がサンプルに含まれている場合、より高速なクエリスタイルが可能です。リンクされた回答の詳細。

4