Why does Active Record trigger an extra query when used. Includes a method for retrieving data.
I have the following model structure:
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
I wanted to keep my database queries to a minimum, so I decided to use include.In the console I tried something like
groups = Group.includes(:products)
my development logs show the following calls,
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
I could analyze the initial three calls that were needed but didn't get the reason the last database call was made,
Product Load (10.0ms) SELECT `products`.* FROM `products` WHERE (`products`.`id` = 377) LIMIT 1
Any idea why this is happening? Thank you in advance.:)
source to share
I don't think the fourth request is coming from this line of code (or the collection it created). Even if you did something like groups.products.find(product_id)
sometime later, you will see a much more complex query with inner joins in the join table. It looks like there should be a separate Product.find(product_id)
or something similar in your code .
source to share
I had a similar problem when releasing a call where()
. The problem was caused by some "lazy evaluation" being done for ActiveRecords::Relation
(like the sailor mentioned above). The solution is to just add a .to_a
at the end of the call. It should work for your expression includes()
above as well. For more information see:
http://daveinabottle.schweisguth.org/2011/05/01/avoiding-extra-queries-in-activerecord-3/
source to share
Now you can easily find an N + 1 request request with the bullet gem (help kill N + 1 requests and unused download.)
Use this gem, it will tell you where you need to work to kill the N + 1 call and also indicate if the download requires.
It helps me to use this and then try it, you get a solution to your problem.
Read the below article explaining to you more about the different ways of doing eager loading -
source to share