2014-10-26 45 views
0

让说我有以下型号:自参照协会:N + 1问题

class Strategy < ActiveRecord::Base 
    belongs_to :user 
    has_many :snapshots, :class_name => 'Strategy', :foreign_key => 'master_id' 
    belongs_to :master, :class_name => 'Strategy', :counter_cache => :snapshots_count 

    scope :master_only, -> { where(:master_id => nil) } 
end 

因此,任何用户都可以创建的Strategy

主实例的快照在控制器我收到的所有“主” 的Strategy实例属于current_user

@strategies = current_user.strategies.master_only.includes(:user,:snapshots) 

滑轨正确地加载strategiessnapshots在两个查询而获取user在单独的查询每个快照因而引入(在此特定情况下N + 2)N + 1个问题:

Strategy Load (0.3ms) SELECT `strategies`.* FROM `strategies` WHERE strategies`.`user_id` = 2 AND `strategies`.`master_id` IS NULL LIMIT 10 OFFSET 0 
User Load (0.7ms) SELECT `users`.* FROM `users` WHERE `users`.`id` IN (2) 
Strategy Load (0.8ms) SELECT `strategies`.* FROM `strategies` WHERE `strategies`.`master_id` IN (56, 8, 55, 1, 58, 57, 24, 22) 
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1 
....  
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 5 LIMIT 1 

有一种方法更有效地加载快照用户?

回答

1

您可以对您有:user:snapshots方式稍有变化,只生成一个查询加载所有属于:snapshots用户:

@strategies = current_user.strategies.master_only.includes(snapshots: :user) 

生成的SQL查询将是这样的:

SELECT `strategies`.* FROM `strategies` WHERE strategies`.`user_id` = 2 AND `strategies`.`master_id` IS NULL LIMIT 10 OFFSET 0 
SELECT `users`.* FROM `users` WHERE `users`.`id` IN (2) 
SELECT `strategies`.* FROM `strategies` WHERE `strategies`.`master_id` IN (56, 8, 55, 1, 58, 57, 24, 22) 
SELECT `users`.* FROM `users` WHERE `users`.`id` IN (2,5) 

UPDATE

根据OP,以下解决了N + 1问题:

@strategies = current_user.strategies.master_only.includes(:user, snapshots: :user) 
+0

太棒了!我不知道我能做'.includes(快照::用户)'。在你提供的表单中它不会改变结果SQL(N + 1仍然发生),但是这个语句解决了这个问题:'@strategies = current_user.strategies.master_only.includes(:user,snapshots::user)'。你能否更新你的答案来反映这一点,所以我可以接受它? – 2014-10-26 19:28:27

+0

谢谢,我已经更新了答案。 – Alireza 2014-10-26 19:47:12