当我使用Includes方法获取数据时,为什么Active Record会触发额外的查询

时间:2012-03-21 10:34:36

标签: ruby-on-rails associations

我有以下模型结构:

class Group < ActiveRecord::Base
  has_many    :group_products, :dependent => :destroy
  has_many    :products, :through => :group_products
end

class Product < ActiveRecord::Base
  has_many  :group_products, :dependent => :destroy
  has_many  :groups, :through => :group_products
end

class GroupProduct < ActiveRecord::Base
  belongs_to :group
  belongs_to :product
end

我想最小化我的数据库查询,所以我决定使用includes。在控制台中我尝试了类似的东西,

groups = Group.includes(:products)

我的开发日志显示以下调用

Group Load (403.0ms)  SELECT `groups`.* FROM `groups`
GroupProduct Load (60.0ms)  SELECT `group_products`.* FROM `group_products` WHERE (`group_products`.group_id IN (1,3,14,15,16,18,19,20,21,22,23,24,25,26,27,28,29,30,33,42,49,51))
Product Load (22.0ms)  SELECT `products`.* FROM `products` WHERE (`products`.`id` IN (382,304,353,12,63,103,104,105,262,377,263,264,265,283,284,285,286,287,302,306,307,308,328,335,336,337,340,355,59,60,61,247,309,311,66,30,274,294,324,350,140,176,177,178,64,240,327,332,338,380,383,252,254,255,256,257,325,326))
Product Load (10.0ms)  SELECT `products`.* FROM `products` WHERE (`products`.`id` = 377) LIMIT 1

我可以分析最初的三次调用是必要的但是没有得到最后一次数据库调用的原因,

 Product Load (10.0ms)  SELECT `products`.* FROM `products` WHERE (`products`.`id` = 377) LIMIT 1

知道为什么会这样吗? 提前致谢。 :)

3 个答案:

答案 0 :(得分:2)

发出where()来电时,我遇到了类似的问题。问题是由于对ActiveRecords::Relation(如上所述的水手)进行了一些“懒惰评估”。解决方案是在呼叫结束时简单地添加.to_a。它也适用于您上面的includes()声明。有关详细信息,请参阅:

http://daveinabottle.schweisguth.org/2011/05/01/avoiding-extra-queries-in-activerecord-3/

答案 1 :(得分:0)

我不认为第四个查询来自这行代码(或由它创建的集合)。即使您稍后在某处执行类似groups.products.find(product_id)的操作,您也会看到一个更复杂的查询,其中连接表上有内部联接。看起来代码中必须有一个单独的Product.find(product_id)或类似的东西。

答案 2 :(得分:-1)

现在,您可以使用bullet gem轻松找到N + 1查询调用(帮助终止N + 1查询和未使用的预先加载。)

使用这个宝石,它会告诉你你需要在哪里解决N + 1呼叫,并告诉你是否需要一个急切加载的地方。

它帮助我很多使用它,然后试一试,你将得到你的问题的解决方案。

请阅读以下文章,了解有关执行预先加载的不同方法的更多信息 -

3 ways to do eager loading (preloading) in Rails 3 & 4