减少Rails查询(N + 1?)

时间:2018-01-13 03:52:05

标签: ruby-on-rails ruby

所以在我过去的应用程序中,我对在Rails中使用.includes有些熟悉,但由于某种原因,我在当前场景中遇到了一些困难。

以下是我正在使用的内容:

    # If non-existent, create. Otherwise, update.
    existing_data = Page.all
    updated_data = {}
    new_records = []
    @latest_page_data.each do |key, value|
      existing_record = existing_data.find_by(symbol: key)
      if existing_record != nil
        updated_data[existing_record.id] = value
      else
        new_records << Page.new(value)
      end
    end

    if !new_records.empty?
      Page.import new_reocrds
    end
    if !updated_data.empty?
      Page.update(updated_data.keys, updated_data.values)
    end
  end

我遇到的问题是代码的.find_by部分导致每@latest_page_data次迭代的查询。我想我会认为existing_data会在内存中保存所需的所有数据,但显然它不会那样工作。

接下来,我尝试过这样的事情:

# If non-existent, create. Otherwise, update.
existing_data = Page.includes(:id, :symbol)
updated_data = {}
new_records = []
@latest_currency_data.each do |key, value|
  existing_record = existing_data.find_by(symbol: key)

但是rails会抛出一个错误,说明:

  

ActiveRecord :: AssociationNotFoundError(名为&#39; id&#39;的关联不是   在页面上找到;也许你拼错了吗?):

所以我无法使用此示例来查找idsymbol属性。

我尝试在:id方法中取出Page.includes,但我需要能够获取ID属性,以便稍后在代码中更新相应的记录。

我还看到了其他一些与此主题有关的帖子,但我认为我可能遇到的问题是我没有处理关联(而且我相信这是{{{{ 1}}是为了?如果是这种情况,还有其他方法可以减少我在这里提交的所有查询吗?

2 个答案:

答案 0 :(得分:2)

includes方法用于预加载关联模型。我认为你要找的是select。修改您的代码以使用select,请执行以下操作:

existing_data = Page.select(:id, :symbol).load
updated_data = {}
new_records = []
@latest_currency_data.each do |key, value|
  existing_record = existing_data.find_by(symbol: key)
  if existing_record
    updated_data[existing_record.id] = value
  else
    new_records << Page.new(value)
  end
end
  

使用select而不是pluck的缺点是,由于Rails为您构造了一个对象,因此它比pluck慢。 Benchmark: pluck vs select

答案 1 :(得分:1)

我没有试图在Rails中找到一种方法(因为我不熟悉100%正确/准确的Rails方式),我只是决定使用.pluck并将其转换为哈希获取我正在寻找的数据:

existing_data = Page.pluck(:id, :symbol)
existing_data = Hash[*existing_data.flatten]
updated_data = {}
new_records = []
@latest_currency_data.each do |key, value|
  if existing_data.values.include? key
    id = existing_data.find{|k,v| v.include? key}[0]
    updated_data[id] = value
  else
    new_records << Page.new(value)
  end
end

如果有人有更好的方式,我们将很高兴地感激不尽。谢谢!