ruby-on-rails - Rails 急于加载所有记录但仍生成 N+1 查询

标签 ruby-on-rails eager-loading

我正在使用 Rails 4.1,并且有一个非常基本的问题让急切加载工作。

我将一个复杂的程序简化为一个非常基本的事件、用户和预订示例:

事件模型:

class Event < ActiveRecord::Base
  has_many :reservations

  def reserved?(user)
    reservations.where(user: user).present?
  end
end

用户模型:

class User < ActiveRecord::Base
  has_many :reservations
end

预订模型:

class Reservation < ActiveRecord::Base
  belongs_to :user
  belongs_to :event
end

我的事件 Controller :

def index
  @events = Event.includes(:reservations).all
  @user = User.find(1)
end

我的views/events/index.html.erb View :

<% @events.each do |event| %>
  <p>Event: <%= event.name %><br>
  Reserved?: <%= event.reserved?(@user) %><br></p>
<% end %>

我创建了一个用户和三个事件。事件索引页的结果服务器日志:

Started GET "/events" for 127.0.0.1 at 2014-12-03 11:26:20 +0800
Processing by EventsController#index as HTML
  User Load (0.1ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
  Event Load (0.1ms)  SELECT "events".* FROM "events"
  Reservation Load (0.2ms)  SELECT "reservations".* FROM "reservations"  WHERE "reservations"."event_id" IN (1, 2, 3)
  Reservation Load (0.1ms)  SELECT "reservations".* FROM "reservations"  WHERE "reservations"."event_id" = ? AND "reservations"."user_id" = 1  [["event_id", 1]]
  Reservation Load (0.0ms)  SELECT "reservations".* FROM "reservations"  WHERE "reservations"."event_id" = ? AND "reservations"."user_id" = 1  [["event_id", 2]]
  Reservation Load (0.1ms)  SELECT "reservations".* FROM "reservations"  WHERE "reservations"."event_id" = ? AND "reservations"."user_id" = 1  [["event_id", 3]]
  Rendered events/index.html.erb within layouts/application (10.0ms)
Completed 200 OK in 76ms (Views: 68.7ms | ActiveRecord: 1.3ms)

如您所见,rails 正确地预先加载了我的事件的所有预订。但是,它会在 reserved?(user) 调用期间再次查询数据库。我究竟做错了什么?谢谢。

最佳答案

您的 reserved? 方法使用 reservations 关联的 where 方法。 where 方法是一个 ARel 方法,用于构建 SQL 查询。对 where 方法的结果调用 present? 会导致执行查询。

为了避免在 reserved? 中进行另一次数据库查找,您应该使用 Ruby 集合方法,例如:

def reserved?(user)
  reservations.select { |reservation| reservation.user == user }.empty?
end

或者,对于更简洁的版本(有人将其添加为评论,但看起来可能已被删除):

def reserved?(user)
  reservations.map(&:user_id).include?(user.id)
end

关于ruby-on-rails - Rails 急于加载所有记录但仍生成 N+1 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27263199/

相关文章:

android - 将 Ruby on Rails 应用程序转换为原生 Android PhoneGap 应用程序

ruby-on-rails - Rails 使模型中的片段缓存过期

ruby-on-rails - Ruby on Rails : Where to define global constants?

ruby-on-rails - 在 Rails Admin 中将字段添加到 'new' View

Django 渴望加载多对多

c# - 为 LINQ 上下文禁用所有延迟加载或强制预加载

ruby-on-rails - 如何先显示特色内容,然后显示范围内的其余内容?

python - GAE 数据存储在 python api 中预先加载

ruby-on-rails - 如何使用 Rails 和 Ancestry 对模型后代执行预先加载

php - Laravel/Eloquent 查询效率: Should a separate table be used for these two columns?