I am trying to achieve by reducing the numbers of queries using ActiveRecord 3.0.9. I generated about ‘dummy’ 200K customers and 500K orders.
Here’s Models:
class Customer < ActiveRecord::Base
has_many :orders
end
class Orders < ActiveRecord::Base
belongs_to :customer
has_many :products
end
class Product < ActiveRecord::Base
belongs_to :order
end
when you are using this code in the controller:
@customers = Customer.where(:active => true).paginate(page => params[:page], :per_page => 100)
# SELECT * FROM customers ...
and use this in the view (I removed HAML codes for easier to read):
@order = @customers.each do |customer|
customer.orders.each do |order| # SELECT * FROM orders ...
%td= order.products.count # SELECT COUNT(*) FROM products ...
%td= order.products.sum(:amount) # SELECT SUM(*) FROM products ...
end
end
However, the page is rendered the table with 100 rows per page. The problem is that it kinda slow to load because its firing about 3-5 queries per customer’s orders. thats about 300 queries to load the page.
There’s alternative way to reduce the number of queries and load the page faster?
Notes:
1) I have attempted to use the includes(:orders), but it included more than 200,000 order_ids. that’s issue.
2) they are already indexed.
If you’re only using
COUNTandSUM(amount)then what you really need is to retrieve only that information and not the orders themselves. This is easily done with SQL:You can wrap this in a method that returns a nice, orderly hash by re-mapping the SQL results into a structure that fits your requirements:
This means you can fetch all order counts and totals in a single pass. If you have an index on
customer_id, which is imperative in this case, then the query will usually be really fast even for large numbers of rows.You can save the results of this method into a variable such as
@order_totalsand reference it when rendering your table: