Как (массово) уменьшить количество SQL-запросов в приложении Rails?
В моем приложении Rails у меня естьusers
которые могут иметь многоinvoices
которые в свою очередь могут иметь многоpayments
.
Сейчас вdashboard
посмотреть я хочу обобщить всеpayments
a user
когда-либо получил, заказал либо по году, кварталу или месяцу.payments
также подразделяются наваловой, сеть, а такженалог.
user.rb:
class User < ActiveRecord::Base
has_many :invoices
has_many :payments
def years
(first_year..current_year).to_a.reverse
end
def year_ranges
years.map { |y| Date.new(y,1,1)..Date.new(y,-1,-1) }
end
def quarter_ranges
...
end
def month_ranges
...
end
def revenue_between(range, kind)
payments_with_invoice ||= payments.includes(:invoice => :items).all
payments_with_invoice.select { |x| range.cover? x.date }.sum(&:"#{kind}_amount")
end
end
invoice.rb:
class Invoice < ActiveRecord::Base
belongs_to :user
has_many :items
has_many :payments
def total
items.sum(&:total)
end
def subtotal
items.sum(&:subtotal)
end
def total_tax
items.sum(&:total_tax)
end
end
payment.rb:
class Payment < ActiveRecord::Base
belongs_to :user
belongs_to :invoice
def percent_of_invoice_total
(100 / (invoice.total / amount.to_d)).abs.round(2)
end
def net_amount
invoice.subtotal * percent_of_invoice_total / 100
end
def taxable_amount
invoice.total_tax * percent_of_invoice_total / 100
end
def gross_amount
invoice.total * percent_of_invoice_total / 100
end
end
dashboards_controller:
class DashboardsController < ApplicationController
def index
if %w[year quarter month].include?(params[:by])
range = params[:by]
else
range = "year"
end
@ranges = @user.send("#{range}_ranges")
end
end
index.html.erb:
<% @ranges.each do |range| %>
<%= render :partial => 'range', :object => range %>
<% end %>
_range.html.erb:
<%= @user.revenue_between(range, :gross) %>
<%= @user.revenue_between(range, :taxable) %>
<%= @user.revenue_between(range, :net) %>
Теперь проблема в том, что этот подход работает, но также генерирует очень много SQL-запросов. В типичномdashboard
посмотреть я получаю100+ SQL-запросы. Перед добавлением.includes(:invoice)
было еще больше запросов.
Я предполагаю, что одной из основных проблем является то, что каждый счетsubtotal
, total_tax
а такжеtotal
нигде не хранятся в базе данных, а рассчитываются с каждым запросом.
Кто-нибудь может сказать мне, как ускорить процесс здесь? Я не слишком знаком с SQL и внутренними принципами работы ActiveRecord, так что, вероятно, проблема здесь.
Спасибо за любую помощь.