Reputation: 663
I have a rails app where I often need to generate xls files with several values.
To do so I load customers orders in my controller (valid
is just a custom scope)
@orders = Order.valid.order('id DESC')
In my view, I want to count meals for each orders with different scopes (i removed all xml tags, which are not interesting here):
<% @orders.each do |order| %>
<%= order.meals.count %>
<%= order.meals.meat.count %>
<%= order.meals.fish.count %>
<%= order.meals.drink.count %>
<%= order.meals.dessert.count %>
<% end %>
My problem is that this generates a very large number of SQL requests.
I tried to preload meals like this:
@orders = Order.valid.order('id DESC').preload(:meals)
But count requests keep being generated
I also found an old gem: preload_count
to do this, but it doesn't works with rails 4.
Is there a way to optimize my requests ?
EDIT
After many tries and help from Andrey Deineko, my request turned into this:
Order.includes(:meals).valid.order('id DESC').references(:meals)
Then i realized it may come from meal types which are in another table in my DB
Order.eager_load(meals: :type).valid.order('orders.id DESC').references(:meals)
But still, here are my db requests:
(0.3ms) SELECT COUNT(*) FROM "meals" INNER JOIN "meals_orders" ON "meals"."id" = "meals_orders"."meal_id" WHERE "meals_orders"."order_id" = $1 [["order_id", 1044]]
(0.3ms) SELECT COUNT(*) FROM "meals" INNER JOIN "meals_orders" ON "meals"."id" = "meals_orders"."meal_id" WHERE "meals_orders"."order_id" = $1 [["order_id", 1044]]
(0.4ms) SELECT COUNT(*) FROM "meals" INNER JOIN "types" ON "types"."id" = "meals"."type_id" INNER JOIN "meals_orders" ON "meals"."id" = "meals_orders"."meal_id" WHERE "meals_orders"."order_id" = $1 AND "types"."name" = $2 [["order_id", 1044], ["name", "meat"]]
(0.4ms) SELECT COUNT(*) FROM "meals" INNER JOIN "types" ON "types"."id" = "meals"."type_id" INNER JOIN "meals_orders" ON "meals"."id" = "meals_orders"."meal_id" WHERE "meals_orders"."order_id" = $1 AND "types"."name" = $2 [["order_id", 1044], ["name", "meat"]]
(0.4ms) SELECT COUNT(*) FROM "meals" INNER JOIN "types" ON "types"."id" = "meals"."type_id" INNER JOIN "meals_orders" ON "meals"."id" = "meals_orders"."meal_id" WHERE "meals_orders"."order_id" = $1 AND "types"."name" = $2 [["order_id", 1044], ["name", "fish"]]
(0.4ms) SELECT COUNT(*) FROM "meals" INNER JOIN "types" ON "types"."id" = "meals"."type_id" INNER JOIN "meals_orders" ON "meals"."id" = "meals_orders"."meal_id" WHERE "meals_orders"."order_id" = $1 AND "types"."name" = $2 [["order_id", 1044], ["name", "fish"]]
(0.3ms) SELECT COUNT(*) FROM "meals" INNER JOIN "types" ON "types"."id" = "meals"."type_id" INNER JOIN "meals_orders" ON "meals"."id" = "meals_orders"."meal_id" WHERE "meals_orders"."order_id" = $1 AND "types"."name" = $2 [["order_id", 1044], ["name", "drink"]]
(0.3ms) SELECT COUNT(*) FROM "meals" INNER JOIN "types" ON "types"."id" = "meals"."type_id" INNER JOIN "meals_orders" ON "meals"."id" = "meals_orders"."meal_id" WHERE "meals_orders"."order_id" = $1 AND "types"."name" = $2 [["order_id", 1044], ["name", "drink"]]
(0.3ms) SELECT COUNT(*) FROM "meals" INNER JOIN "types" ON "types"."id" = "meals"."type_id" INNER JOIN "meals_orders" ON "meals"."id" = "meals_orders"."meal_id" WHERE "meals_orders"."order_id" = $1 AND "types"."name" = $2 [["order_id", 1044], ["name", "dessert"]]
Upvotes: 0
Views: 312
Reputation: 2927
If your models have a has_many belongs_to relationship you can use includes to eager load the associations. This is a good article on eager loading.
Upvotes: 0
Reputation: 52377
go with
@orders = Order.includes(:meals).valid.order('id DESC')
if you want to have only orders with meals (no orders, where meal is nil
) go with:
@orders = Order.joins(:meals).valid.order('id DESC')
Have a read about AR querying.
Upvotes: 1