1

このクエリはすでに完全に実行されていますが、問題は、4 つのテーブルが大きくなりすぎて、かなり遅くなることです。

これを最適化するにはどうすればよいですか?

SELECT 
    all_records.user_id,
    users.NAME,
    users.IMAGE
FROM (
    SELECT user_id FROM comments
    WHERE commentable_id   = #{object.id}
      AND commentable_type = '#{object.class.to_s}'
    UNION ALL
    SELECT user_id FROM hello
    WHERE helloable_id     = #{object.id}
      AND helloable_type   = '#{object.class.to_s}'
    UNION ALL
    SELECT user_id FROM foo
    WHERE fooable_id       = #{object.id}
      AND fooable_type     = '#{object.class.to_s}'
    UNION ALL
    SELECT user_id FROM bar
    WHERE barable_id       = #{object.id}
      AND barable_type     = '#{object.class.to_s}'
) AS all_records
INNER JOIN users ON users.id = all_records.user_id
GROUP BY
    all_records.user_id,
    users.NAME,
    users.IMAGE
LIMIT 15

クエリが行うべきことは、(4) テーブルで何かを行った一意のユーザーを取得することです (テーブルの名前の変更を許してください)。それでもLIMIT 154つのテーブルすべてを読み取っていると思うので、それでも動作が遅くなります。私はこれを正しく行っていますか、またはこれを最適化する方法はありますか?

参考までに:私はpostgresを使用しており、railsを使用していますが、で実行していfind_by_sqlます。

編集

ローカル ポストグル: 9.0.5; ヘロクポストグル: 9.1

4

1 に答える 1

1

あなたの質問をそのまま受けてください:「15個の任意の行を取得してください」。それは非常に速いはずです。

SELECT u.id, u.name, u.image
FROM  (
   SELECT id
   FROM  (
      SELECT user_id AS id
      FROM   comments
      WHERE  commentable_id   = #{object.id}
      AND    commentable_type = '#{object.class.to_s}'

      UNION ALL
      SELECT user_id
      FROM   hello
      WHERE  helloable_id   = #{object.id}
      AND    helloable_type = '#{object.class.to_s}'

      UNION ALL
      SELECT user_id
      FROM   foo
      WHERE  fooable_id     = #{object.id}
      AND    fooable_type   = '#{object.class.to_s}'

      UNION ALL
      SELECT user_id
      FROM   bar
      WHERE  barable_id     = #{object.id}
      AND    barable_type   = '#{object.class.to_s}'
      ) AS a
   GROUP  BY id
   LIMIT  15
   ) b
JOIN   users u USING (id)
  • PostgreSQL 9.1 以降を実行している場合は、 が主キーであるGROUP BY idと仮定して、 に簡略化できます。users.idしかし、私はもっと過激なアプローチをとっています。

  • ベース テーブルのインデックス スキャンを高速化するために、クエリ レベルGROUP BYを1 つ引き上げます。LIMITaLIMIT 15および no を使用すると、ORDER BYシーケンシャル スキャンは発生しません。Postgres は、インデックスの先頭からタプルを読み取るだけで、制限に達するとすぐに停止します。
    この密接に関連したケースに似ています:結果が利用可能になるまで複数の SELECT を試す方法は?
    ここでのみ、Postgres はインデックスからタプルを読み取ります。

  • は結果から行を削除する可能性があり、より単純なクエリ プランを無効にする可能性があるため、代わりに(余分なサブクエリ レベルの代わりに)mightを使用することで同じ効果が得られます。LEFT JOIN usersJOINJOIN

  • 完璧なパフォーマンスを得るには、次のようなインデックスがあります

    CREATE INDEX comments_mult_idx
    ON comments (commentable_id, commentable_type, user_id)
    

    4つのテーブルすべてに。user_id最後の列でなければなりません。理由は次のとおりです。

于 2013-02-12T14:53:12.897 に答える