5

私は、コントローラーで作成した長蛇の列のロジックを使用するのではなく、スコープを使用してArelクエリをチェーンしようとしてきました。ただし、スコープは、すべてのレコードを取得して、ロジックを使用してそれらを選別するよりも低速です。では、なぜスコープが優れているのか疑問に思います。

これが私がしていることです:

  • 質問には多くの答えがあります
  • 答えは1つの質問に属します
  • 質問には、並べ替えに使用する「question_type」列があります

まず、スコープの方法...

質問.rb:

scope :answered, joins(:answers).order('answers.created_at desc')
scope :dogs, where(:question_type => "dogs")
scope :cats, where(:question_type => "cats")
scope :mermaids, where(:question_type => "mermaids")

question_controller.rb:

@dogs_recently_answered = Question.answered.dogs.uniq[0..9]
@cats_recently_answered = Question.answered.cats.uniq[0..9]
@mermaids_recently_answered = Question.answered.mermaids.uniq[0..9]

次に、ビューで、これらのインスタンス変数(現在は最大10個の要素を含む配列)を循環し、結果を表示します。

ページの読み込みにかかる時間(5つの異なる時間)は次のとおりです。

535msで200OKを完了(ビュー:189.6ms | ActiveRecord:46.2ms)

573msで200OKを完了(ビュー:186.0ms | ActiveRecord:46.3ms)

577msで200OKを完了(ビュー:189.0ms | ActiveRecord:45.6ms)

532msで200OKを完了(ビュー:182.9ms | ActiveRecord:46.1ms)

577msで200OKを完了(ビュー:186.7ms | ActiveRecord:46.9ms)

さて、厄介なコントローラーの方法...

@answers = Answer.order("created_at desc")
@all_answered = []
@answers.each {|answer| @all_answered << answer.question}
@recently_answered = @all_answered.uniq
@dogs_all_answered = []
@cats_all_answered = []
@mermaids_all_answered = []
@recently_answered.each do |q|
  if q.question_type == "dogs"
    @dogs_all_answered << q
    @dogs_recently_answered = @dogs_all_answered[0..9]
  elsif q.question_type == "cats"
    @cats_all_answered << q
    @cats_recently_answered = @cats_all_answered[0..9]
  elsif q.question_type == "mermaids"
    @mermaids_all_answered << q
    @mermaids_recently_answered = @mermaids_all_answered[0..9]
  end
end

そして、これが今ページをロードするのにかかる時間です(5つの異なる時間):

475msで200OKを完了(ビュー:196.5ms | ActiveRecord:34.5ms)

480msで200OKを完了(ビュー:200.4ms | ActiveRecord:36.4ms)

434msで200OKを完了(ビュー:198.2ms | ActiveRecord:35.8ms)

475msで200OKを完了(ビュー:194.2ms | ActiveRecord:36.4ms)

475msで200OKを完了(ビュー:195.0ms | ActiveRecord:35.4ms)

それで...

読みやすさは別として、スコープでクエリを磨くことで何が得られるでしょうか?レコードが増えると、最終的には速くなりますか?

4

2 に答える 2

5

まず、質問が一意以外の方法である可能性があるかどうかわからないので、それを削除することを検討します。私はあなたのデータの論理を知らないので、それは当てはまらないかもしれませんが、それはあなたが避けることができるかもしれない余分なステップです。

これが私がそれにアプローチする方法です:

scope :answered, joins(:answers).order('answers.created_at desc')
scope :recent, take(10)
scope :dogs, where(:question_type => "dogs")
scope :cats, where(:question_type => "cats")
scope :mermaids, where(:question_type => "mermaids")

@dogs_recently_answered = Question.answered.dogs.recent
@cats_recently_answered = Question.answered.dogs.recent
@mermaids_recently_answered = Question.answered.dogs.recent

これにより、クエリの一部が、すべてTOPの行をフェッチしてから10を除くすべてを破棄するのではなく、それが属するデータベースにシフトされます。一意の基準によっては、次のようなスコープを使用することもできます。

scope :unique, select('DISTINCT column_name')

次に、Question.cats.unique.recentを使用して、データベースシステムが設計されている関係代数を活用する1つの高速クエリですべてを取得できます。

于 2010-11-30T14:40:01.230 に答える
1

この場合、スコープが遅くなる理由は、3つの個別のデータベースクエリが生成されるためだと思いますが、他のアプローチでは、使用する1つのクエリで3つの結果すべてを満たすことができるという知識を使用します。

それが事実であると仮定すると、スコープが3つの別々のクエリを実行していることは驚くべきことではありません。システムは、最初のクエリをいつ呼び出すか、すぐに他のクエリを呼び出すかを知らないからです。このシナリオに適した最適化戦略があるかもしれませんが、ActiveRecordがそれを実装していることはわかりません。

とにかく、これはこの特定の場合のスコープの1つの欠点です。スコープはクリーン/クリアで柔軟性があり、クエリの名前付き抽象化がカプセル化されているため、私はスコープが好きです。AFAICT、多くのシナリオでは、同等の直接クエリよりもそれほど遅くはありません。

于 2010-11-30T14:20:05.787 に答える