2017-01-10 5 views
0

は、私は単純なクエリを実行します。のPostgreSQL&ActiveRecordのスロークエリ

私はCHANNEL_IDのためのすべての履歴を選択した場合= 1:

History.where(channel_id: 1) 

History Load (0.5ms) SELECT "histories".* FROM "histories" WHERE "histories"."channel_id" = 1 [["channel_id", 1]] 

それだけで0.5msの

を取り、我々はRubyの配列の助けを借りて、一つのレコードを取るしようとした場合:

History.where(channel_id: 1).order('histories.id DESC').to_a.first 

History Load (0.5ms) SELECT "histories".* FROM "histories" WHERE "histories"."channel_id" = 1 ORDER BY id DESC [["channel_id", 1]] 

どこに問題があるのでしょうか?

PS:既にchannel_idフィールドにインデックスがあります。

UPD:

History.where(channel_id: 1).order('histories.id DESC').limit(1).explain 
    History Load (848.9ms) SELECT "histories".* FROM "histories" WHERE "histories"."channel_id" = 1 ORDER BY histories.id DESC LIMIT 1 [["channel_id", 1]] 
=> EXPLAIN for: SELECT "histories".* FROM "histories" WHERE "histories"."channel_id" = 1 ORDER BY histories.id DESC LIMIT 1 [["channel_id", 1]] 
               QUERY PLAN 
------------------------------------------------------------------------------------------------------- 
Limit (cost=0.43..13.52 rows=1 width=42) 
    -> Index Scan Backward using histories_pkey on histories (cost=0.43..76590.07 rows=5849 width=42) 
     Filter: (channel_id = 1) 
(3 rows) 
+1

通常、channel_id、idの複合インデックスが役立ちます。あなたのユースケースが特別な場合、つまりチャネル '1'だけが必要な場合、[部分インデックス](https://www.postgresql.org/docs/current/static/indexes-partial.html)を設定することができます。 。 – pozs

+0

@pozsコンパウンドインデックス 'channel_id、id'で解決していただきありがとうございます – zolter

答えて

0

PostgreSQLは(ORDER BYとLIMIT句で)あなたのクエリを処理することができる2つの方法があります。

  • は、その後、それがテーブルをスキャンし、見つかったタプルを注文することができますあなたの結果を制限する。 PostgreSQLは、あなたのテーブルのタプル数が非常に少ないと思ったり、インデックスが役に立たないと思ったりすると、この計画を選択します。
  • インデックスを使用できます。

PostgreSQLが私の愚見では2つだけの理由で発生する可能性があります最初のオプションを選択しなかったようだ:

  • あなたのテーブルの統計情報は正確ではありません。
  • channel_idの値が不均等に分布している(たとえば、ほとんどすべてのタプルにchannel_id = 2が設定されている)ので、PostgreSQLではインデックスが役に立たないと思うのはこのためです。ここで私は部分的なインデックスの使用をお勧めします。
関連する問題