如何获取关联模型的最后一条记录以防止 N+1 查询?
How to fetch an associated model's last record to prevent N+1 queries?
我有一个模型 Invoice
has_many
Payments
和一个模型 Payment
belongs_to
Invoice
.
我们每月批量导出发票数据,需要每张发票的最后一笔付款。
在我们看来,我们目前正在为每个要导出的发票执行一次 Invoice.payments.last
,我被要求防止 N+1 次查询。
我不明白是否应该在控制器或发票模型中添加此查询,或者它是否应该是 has_one :last_payment
关联或范围。
如有任何帮助,我们将不胜感激。
如果每张发票的付款次数相对较少,您可以 include/eager_load/preload 协会:
invoices = Invoice.includes(:payments)
invoices.each do |i|
puts i.payments.last.amount # no n+1 query
end
然而,这会立即将所有相关记录加载到内存中。这可能会导致性能问题。
一个非常高效的读取优化是将外键列添加到发票 table 和一个 belongs_to 关联,您可以在预先加载时使用它:
class AddLatestPaymentToInvoices < ActiveRecord::Migration[6.0]
def change
add_reference :invoices, :latest_payment, null: false, foreign_key: { to_table: :payments }
end
end
class Invoice < ApplicationRecord
has_many :payments, after_add: :set_latest_invoice!
belongs_to :latest_payment,
class_name: 'Payment'
private
def set_latest_payment(payment)
update_columns(latest_payment_id: payment.id)
end
end
invoices = Invoice.includes(:latest_payment)
invoices.each do |i|
puts i.latest_payment.amount # no n+1 query
end
成本是每条插入记录的额外更新查询。可以使用 DB 触发器而不是 association callback.
对其进行优化
我有一个模型 Invoice
has_many
Payments
和一个模型 Payment
belongs_to
Invoice
.
我们每月批量导出发票数据,需要每张发票的最后一笔付款。
在我们看来,我们目前正在为每个要导出的发票执行一次 Invoice.payments.last
,我被要求防止 N+1 次查询。
我不明白是否应该在控制器或发票模型中添加此查询,或者它是否应该是 has_one :last_payment
关联或范围。
如有任何帮助,我们将不胜感激。
如果每张发票的付款次数相对较少,您可以 include/eager_load/preload 协会:
invoices = Invoice.includes(:payments)
invoices.each do |i|
puts i.payments.last.amount # no n+1 query
end
然而,这会立即将所有相关记录加载到内存中。这可能会导致性能问题。
一个非常高效的读取优化是将外键列添加到发票 table 和一个 belongs_to 关联,您可以在预先加载时使用它:
class AddLatestPaymentToInvoices < ActiveRecord::Migration[6.0]
def change
add_reference :invoices, :latest_payment, null: false, foreign_key: { to_table: :payments }
end
end
class Invoice < ApplicationRecord
has_many :payments, after_add: :set_latest_invoice!
belongs_to :latest_payment,
class_name: 'Payment'
private
def set_latest_payment(payment)
update_columns(latest_payment_id: payment.id)
end
end
invoices = Invoice.includes(:latest_payment)
invoices.each do |i|
puts i.latest_payment.amount # no n+1 query
end
成本是每条插入记录的额外更新查询。可以使用 DB 触发器而不是 association callback.
对其进行优化