1

私のRailsアプリが非常に多くのSQLステートメントを生成していることにターミナル経由で気づき始めましたが、その多くは必須ではありません。どこかでこれが問題になる可能性があることを読んだことを覚えています。現在、データが大きくなると、アプリの速度が著しく低下します。

たとえば、トラックのあるリリースがあります。アーティストは、トラックとリリースの両方に割り当てることができます。トラックが8つしかないリリースをロードすると、DB内のすべてのトラックを実行して、それらの関係を見つけているようです!?!

たとえば、以下を参照してください。これは非常に小さなサンプルですが、これらのトラックは実際にはリリースに関連付けられていません。DB内のすべてのトラックを通過しています!!

一般的な指針はありますか?

Artist Load (0.6ms)  SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 12
  Artist Load (0.6ms)  SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 19
  Artist Load (0.6ms)  SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 21
  Artist Load (0.9ms)  SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 22
  Artist Load (0.7ms)  SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 23
  Artist Load (0.6ms)  SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 24
  Artist Load (0.9ms)  SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 25
  Artist Load (1.0ms)  SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 26
  Artist Load (0.6ms)  SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 27
  Artist Load (0.9ms)  SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 28
  Artist Load (0.6ms)  SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 29
  Artist Load (0.6ms)  SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 30
  Artist Load (0.6ms)  SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 31

関連するモデルは次のとおりです。

class Artist < ActiveRecord::Base  
  has_many :artist_releases
  has_many :releases, :through => :artist_releases 
  has_many :artists_tracks
  has_many :tracks, :through => :artists_tracks
end

class ArtistRelease < ActiveRecord::Base
  belongs_to :artist
  belongs_to :release
end

class ArtistsTrack < ActiveRecord::Base
  belongs_to :artist
  belongs_to :release
  belongs_to :track
end

class Release < ActiveRecord::Base
  has_many :artist_releases
  has_many :artists, :through => :artist_releases
  accepts_nested_attributes_for :artists, :reject_if => lambda { |a| a[:name].blank? }
  accepts_nested_attributes_for :artist_releases    
  has_many :releases_tracks, :dependent => :destroy
  has_many :tracks, :through => :releases_tracks, :order => "releases_tracks.position"
  accepts_nested_attributes_for :tracks, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => :true
  accepts_nested_attributes_for :releases_tracks    
end

class ReleasesTrack < ActiveRecord::Base
  default_scope :order => 'releases_tracks.position ASC'
  acts_as_list :scope => :release_id
  belongs_to :release
  belongs_to :track
end

class Track < ActiveRecord::Base
  has_many :releases_tracks, :dependent => :destroy
  has_many :releases, :through => :releases_tracks
  has_many :artists_tracks
  has_many :artists, :through => :artists_tracks
  accepts_nested_attributes_for :artists, :reject_if => lambda { |a| a[:name].blank? }
  accepts_nested_attributes_for :artists_tracks
end
4

1 に答える 1

5

Railsが生成しているクエリから、次のようなことをしているように見えます。これは、参照すると各アーティストを個別にロードします。

release.tracks.each{ |t| t.artist }

単一のクエリでアーティストが事前に読み込まれるように、アーティストを熱心に読み込む必要があります。

release.tracks.includes(:artist).each{ |t| t.artist }

このincludes方法は非常に柔軟性があり、複数の関連付け、またはネストされた関連付けを熱心にロードできます。

Release.first.includes(:releases_tracks => {:track => :artist})

これにより、最初のリリース、次にすべてのReleaseTracks、次にすべてのトラック、およびすべてのトラックのアーティストが4つのクエリ(テーブルごとに1つ)でロードされます。これは、各レコードを個別にロードするよりもはるかに効率的です。

詳細については、ActiveRecordQueryインターフェイスガイドのEagerLoadingAssociationsのセクションを参照してください。

于 2012-05-31T16:32:36.803 に答える