簡體   English   中英

在 Rails 查詢中防止 N+1 與關聯的 model

[英]Preventing N+1 in Rails query with associated model

我有這些模型:

class KlassOccurrence < ApplicationRecord
  belongs_to :klass
end

class Klass < ApplicationRecord
  has_many :klass_occurrences
  belongs_to :user

  def next_occurrence_date
    self.klass_occurrences.order(scheduled_at: :desc).first
  end
end

我在 controller 中有這個:

@klasses = Klass.includes(:klass_occurrences).where(user_id: current_user.id)

在一個視圖中,我有這個:

   <% @klasses.each do |klass| %>
      <tr>
        <td><%= klass.next_occurrence_date %></td>
      </tr>
    <% end %>

我希望由於includes(:klass_occurrences)我不會看到 N+1,但這是我在日志中看到的:

User Load (0.4ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 3], ["LIMIT", 1]]
  ↳ app/controllers/klasses_controller.rb:4:in `index'
  Rendering klasses/index.html.erb within layouts/dashboard_layout
  Klass Load (0.2ms)  SELECT "klasses".* FROM "klasses" WHERE "klasses"."user_id" = $1  [["user_id", 3]]
  ↳ app/views/klasses/index.html.erb:13
  KlassOccurrence Load (4.3ms)  SELECT "klass_occurrences".* FROM "klass_occurrences" WHERE "klass_occurrences"."klass_id" IN ($1, $2)  [["klass_id", 1], ["klass_id", 5]]
  ↳ app/views/klasses/index.html.erb:13
  KlassOccurrence Load (1.8ms)  SELECT "klass_occurrences".* FROM "klass_occurrences" WHERE "klass_occurrences"."klass_id" = $1 ORDER BY "klass_occurrences"."scheduled_at" DESC LIMIT $2  [["klass_id", 1], ["LIMIT", 1]]
  ↳ app/models/klass.rb:6:in `next_occurrence_date'
  KlassOccurrence Load (0.3ms)  SELECT "klass_occurrences".* FROM "klass_occurrences" WHERE "klass_occurrences"."klass_id" = $1 ORDER BY "klass_occurrences"."scheduled_at" DESC LIMIT $2  [["klass_id", 5], ["LIMIT", 1]]
  ↳ app/models/klass.rb:6:in `next_occurrence_date'

為什么它對 klass 事件進行三個查詢? 我希望只有一個查詢來加載所有類的所有事件,但隨后我看到了兩個額外的查詢。

如果您只需要該列的最大值,您也可以只 select 連接表中的一個聚合:

class Klass < ApplicationRecord
  has_many :klass_occurrences
  belongs_to :user

  def self.with_next_occurrence_date
    self.select(
         'klass.*',
         'MAX(klass_occurrences.scheduled_at) AS next_occurrence_date'
        )
        .left_joins(:klass_occurrences)
        .group(:id)
  end 
end
@klasses = Klass.with_next_occurrence_date.where(user_id: current_user.id)
<% @klasses.each do |klass| %>
  <tr>
    <td><%= klass.next_occurrence_date %></td>
  </tr>
<% end %>

如果您真的需要 model object,您可以通過對 Ruby 中的記錄進行排序來實現:

class Klass < ApplicationRecord
  has_many :klass_occurrences
  belongs_to :user
  def next_occurrence_date
    self.klass_occurrences.sort(&:scheduled_at).last
  end
end

還有一些新的技巧可以讓 ActiveRecord 從關聯中加載一條記錄,這些記錄在這個問題上有點超出 scope。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM