如何从 Ruby on Rails 中的数据库查询中消除 N+1 查询?

Posted

技术标签:

【中文标题】如何从 Ruby on Rails 中的数据库查询中消除 N+1 查询?【英文标题】:How to eliminate N+1 queries from database query in Ruby on Rails? 【发布时间】:2022-01-13 10:11:28 【问题描述】:

在我的 Rails 6 应用程序中,我有这些模型:

class User < ApplicationRecord

  has_many :read_news_items
  has_many :news_items, :through => :read_news_items

end

class NewsItem < ApplicationRecord

  has_many :read_news_items
  has_many :users, :through => :read_news_items

  def read?(user)
    read_news_items.where(:user_id => user.id).any?
  end

end

class ReadNewsItem < ApplicationRecord

  belongs_to :user
  belongs_to :news_item

end

在我的控制器操作中,我想列出所有新闻项目并突出显示用户尚未阅读的新闻:

class NewsItemsController < ApplicationController

  def index
    @news_items = NewsItem.all
  end

end

问题在于,这会为每个记录生成 N+1 个查询,因为每个 user 记录都会调用 read?(current_user)

如何解决这个问题?

我尝试将includes(:read_news_items)joins(:read_news_items) 附加到控制器中的数据库查询中,但无济于事。

【问题讨论】:

读取情况如何?(current_user) 被调用了? 你想运行什么查询。你能分享一下日志吗 下一个怎么样:@news_items = NewsItem.where.not(id: @user. read_news_items.pluck(:new_items_id)?并且您不需要在视图中调用read? @kunashir 所说的除了你不应该打电话给pluck,改用selectselect 将创建一个带有子查询的 IN 子句; pluck 将创建一个带有一次性 Array 的 IN 子句 【参考方案1】:

你可以试试:

class NewsItem < ApplicationRecord
  has_many :read_news_items

  def read?(user)
    if read_news_items.loaded?
      read_news_items.any? |rni| rni.user_id == user.id 
    else
      read_news_items.where(:user_id => user.id).any?
    end
  end
end

class NewsItemsController < ApplicationController
  def index
    @news_items = NewsItem.includes(:read_news_items).all
  end
end

【讨论】:

谢谢。我不知道loaded? 方法。【参考方案2】:

好的,我从这里给出的每个答案中都学到了一些东西。非常感谢。

我将read? 方法更改为以下似乎消除了N+1 个查询:

class NewsItem < ApplicationRecord

  def read?(user)
    user.read_news_items.pluck(:news_item_id).include?(id)
  end

end

【讨论】:

user.read_news_items.pluck 周围的括号不需要。 谢谢,我刚刚更新了我的代码。

以上是关于如何从 Ruby on Rails 中的数据库查询中消除 N+1 查询?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Ruby on Rails 中查看给定 ActiveRecord 查询将生成的 SQL

如何从 Ruby on Rails 中的字符串值创建变量名?

如何从 ruby​​ on rails 上的现有远程 oracle 数据库中获取数据

使用 Ruby on Rails 从数据库中的 yaml 序列化字段中获取大小数

如何在 ruby​​ on rails 中发布数据

如何在 ruby​​ on rails 中编写嵌套查询?