0

質問は長くなるかもしれませんが、問題は実際には単純です。リスト、外観、ムービーの 3 つのモデルがあります。List には Appearance を介して多数のムービーがあり、結合モデルの属性ランクによってソートする必要があります。では、どのインデックスを使用すればよいでしょうか? これは私のモデルが現在どのように見えるかです:

# Models
class Movie < ActiveRecord::Base  
  has_many :appearances, :dependent => :destroy
  has_many :lists, :through => :appearances
end

class Appearance < ActiveRecord::Base
  belongs_to :list
  belongs_to :movie
end

class List < ActiveRecord::Base
  has_many :appearances, :dependent => :destroy
  has_many :movies, :through => :appearances

  def self.find_complete(id)
    List.includes({:appearances => :movie}).where("appearances.rank IS NOT NULL").order("appearances.rank ASC").find(id)
  end
end

そして、ここに私がすでに持っているインデックスがあります。複合インデックスは必要ですか? それともランク別のインデックスですか?

# Tables
class CreateAppearances < ActiveRecord::Migration
  def change
    create_table :appearances do |t|
      t.integer :list_id, :null => false
      t.integer :movie_id, :null => false
      t.integer :rank
    end
    add_index :appearances, :list_id
    add_index :appearances, :movie_id
  end
end

最後に、その find_complete List メソッドをリファクタリングする方法はありますか? default_scope を使用するのが好きですか? ランクが null になる可能性があることを忘れないでください。

4

3 に答える 3

1

ActiveRecord は 2 つのクエリを実行しています。

SELECT DISTINCT `lists`.id
FROM `lists` LEFT OUTER JOIN `appearances` ON `appearances`.`list_id` = `lists`.`id`
             LEFT OUTER JOIN `movies` ON `movies`.`id` = `appearances`.`movie_id`
WHERE `lists`.`id` = 1 AND (appearances.rank IS NOT NULL)
ORDER BY appearances.rank ASC LIMIT 1

SELECT `lists`.`id` AS t0_r0, `lists`.`name` AS t0_r1, `lists`.`created_at` AS t0_r2, `lists`.`updated_at` AS t0_r3, `appearances`.`id` AS t1_r0, `appearances`.`list_id` AS t1_r1, `appearances`.`movie_id` AS t1_r2, `appearances`.`rank` AS t1_r3, `appearances`.`created_at` AS t1_r4, `appearances`.`updated_at` AS t1_r5, `movies`.`id` AS t2_r0, `movies`.`name` AS t2_r1, `movies`.`created_at` AS t2_r2, `movies`.`updated_at` AS t2_r3
FROM `lists` LEFT OUTER JOIN `appearances` ON `appearances`.`list_id` = `lists`.`id`
             LEFT OUTER JOIN `movies` ON `movies`.`id` = `appearances`.`movie_id`
WHERE `lists`.`id` = 1 AND `lists`.`id` IN (1) AND (appearances.rank IS NOT NULL)
ORDER BY appearances.rank ASC

どちらの場合も、必要なのは、lists.id とappearances.rank の両方を持つインデックスです。一部のデータベースは、複数のテーブルにインデックスを作成します。また、あなたのデータベースがappearances.list_idとappearances.rankのインデックスを使用できるほどスマートかどうかもわかりませんが、試してみる価値はあります。

add_index :appearances, [:list_id, :rank]

それでも問題が解決しない場合は、少なくとも index :rank を使用すると、データベースがソートを回避できる可能性があります。

于 2012-05-22T16:18:36.110 に答える
0

関連付けを再確認してください。もしも

リストには、外観を介して多くの映画があります

class List < ActiveRecord::Base
 has_many :appearances, :dependent => :destroy
 has_many :movies, :through => :appearances
end

class Appearance < ActiveRecord::Base
 belongs_to :list, :foreign_key => list_id
 has_many :movies
end

class Movie < ActiveRecord::Base  
 belongs_to :appearance, :foreign_key => appearance_id, :dependent => :destroy  
end

find_complete メソッドに関しては、なぜそれをスコープに変えるのかわかりません。おそらく、すでに List モデル内にいて、 List.includes.... 自体の呼び出しが非常に奇妙に見えるため、少し短くする必要があります。

その movie_id インデックスの必要性がわかりません

class CreateAppearances < ActiveRecord::Migration
  def change
    create_table :appearances do |t|
      t.integer :list_id, :null => false
      t.integer :movie_id, :null => false
      t.integer :rank
    end
  add_index :appearances, :list_id
 end
end

また、movies テーブルの Appearance_id インデックスも必要です。

何も見逃していないことを願っています

于 2012-05-22T15:42:13.740 に答える
0

特定のユースケースでRailsが出力するクエリを確認する必要があります。

私の最善の推測は、外部キー列にインデックスを追加し、アプリが完了してデータがロードされたらプロファイラーを使用して、特定のクエリのパフォーマンスが低下しているかどうかを確認し、修正することです!

データベースの裏をかこうとするのではなく、単に測定して修正するだけです。それ以外の方法を使用すると、節約できる以上のパフォーマンスが犠牲になる可能性があります。

あなたがここで求めているのは、時期尚早な最適化であり、少し当て推量でもあります.Rails3 Arelが実行をどのように延期するかを考えると、実際にどのインデックスが必要になるかは、この関連付けがどのように使用され、どのようにトラバースされるかによって大きく異なります.

于 2012-05-22T15:29:25.177 に答える