Обычно пишу SQL, но тут попросили поправить отчёт в админке, который считает выручку по категориям за месяц. Открывается 14 секунд. Код:
orders = Order.where(created_at: month_range).includes(:items)
totals = Hash.new(0)
orders.each do |order|
order.items.each do |item|
totals[item.category_id] += item.price * item.quantity
end
end
Заказов за месяц около 120 тысяч, позиций под 400 тысяч. Понимаю, что считать надо в базе, но как это записать в Active Record, не уходя в голый SQL?
Сейчас вы тянете в Ruby больше полумиллиона объектов, чтобы сложить числа. Группировка в базе:
totals = OrderItem
.joins(:order)
.where(orders: { created_at: month_range })
.group(:category_id)
.sum(Arel.sql("order_items.price * order_items.quantity"))
Один запрос, на выходе хэш { category_id => сумма }, вьюху переделывать почти не придётся. Проверьте ещё индексы на orders.created_at и order_items.order_id.
Сохранила, очень наглядно, как два вложенных each превращаются в один запрос
Если отчёт смотрят в основном за прошлые месяцы, а они уже не меняются, считайте итоги раз в сутки в отдельную таблицу и показывайте оттуда. Текущий месяц можно считать на лету, как предложил Тимур, остальное брать готовыми строками.
и month_range лучше брать через Time.current.all_month, а не собирать руками: с датами вместо времени легко потерять последний день месяца