Rails执行太多的SQL

问题描述:

我已经开始注意到,我的Rails应用程序生成太多的SQL语句,其中很多都不是必需的。我记得在某个地方读这可能是一个问题,而且现在随着我的数据增长,它明显地减缓了应用程序的速度。Rails执行太多的SQL

例如,我有发布有轨道。艺术家可以分配到曲目和发行。当我加载一个只有8首曲目的发行版时,它似乎正在贯穿数据库中的每一首曲目,以找到这些关系!?!

例如,请参见下文,这是一个非常小的示例,但这些轨道都没有与发布实际关联。它正在通过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 
+0

可能希望包含关系的基础知识(模型定义)。 –

+0

确保您不会循环查询。所有这些查询都应该被转换成一个IN语句 – Limey

+0

@DaveNewton - 我会尽量在一秒内添加,所有这些都是非常重要的,所以为了清晰起见,我必须删除一些内容。 – Raoot

从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张)。这比单独加载每条记录效率更高。

有关Eager Loading Associations in the Active Record Query interface guide部分的详细信息。

+0

啊! 'release.tracks.each {| t | t.artist}'看起来很熟悉,所以我会看看我是否可以按照你的建议重构。谢谢! – Raoot

+0

实际上并不完全一样,在我看来,tracklisting是通过'',然后艺术家用'。我应该在其他地方做点什么吗? – Raoot

+0

我怀疑当你最初加载'@ release'时想使用'includes',就像:'@release = Release.foo.includes(:releases_tracks => {:track =>:artists})'但是如果你更新您的问题与更多关于您的模型的信息(以及您在评论中发布的代码)我或其他人将能够提供更好的答案。 – georgebrock