【问题标题】:How to fetch an associated model's last record to prevent N+1 queries?如何获取关联模型的最后一条记录以防止 N+1 查询?
【发布时间】:2021-04-07 21:23:23
【问题描述】:

我有一个模型Invoice has_many Payments 和一个模型Payment belongs_to Invoice

我们每月分批导出Invoice数据,我们需要每张Invoice的最后一次Payment。

在我们看来,我们目前正在为要导出的每个 Invoice 执行一次 Invoice.payments.last,并且我被要求阻止 N+1 查询。

我不明白是否应该在控制器或 Invoice 模型中添加此查询,或者它应该是 has_one :last_payment 关联还是范围。

任何帮助将不胜感激。

【问题讨论】:

    标签: ruby-on-rails postgresql model associations


    【解决方案1】:

    如果每张发票的付款次数相对较少,您可以只包含/eager_load/preload 关联:

    invoices = Invoice.includes(:payments)
    invoices.each do |i|
      puts i.payments.last.amount # no n+1 query
    end
    

    但是,这会将所有关联的记录一次加载到内存中。这可能会导致性能问题。

    一个非常高效的读取优化是将外键列添加到发票表和一个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
    

    成本是插入的每条记录的额外 UPDATE 查询。它可以通过使用 DB 触发器而不是 association callback 来优化。

    【讨论】:

    • 谢谢!这让我有很多工作要做!
    猜你喜欢
    • 2021-12-24
    • 2013-12-31
    • 2021-04-17
    • 1970-01-01
    • 2021-09-28
    • 1970-01-01
    • 2017-05-08
    • 2011-01-12
    • 1970-01-01
    相关资源
    最近更新 更多