如何避免此代码的N + 1查询

时间:2018-01-29 04:32:31

标签: ruby-on-rails activerecord

我一直在努力弄清楚如何避免使用此代码中的这么多SQL查询。我通过带有Shopify的API接收Collect对象,然后检查我的本地数据库中是否已匹配,如果没有,则创建一个新的本地Collect对象,其中包含来自我本地匹配产品和集合的参数。我不确定如何使用包含或加入,因为我正在进行查找而不仅仅是在循环中获取值。欢迎提出任何指示。

I2 = flt_rot(double(t2):end,:);

模式

  def seed_collects!
    shop = self.with_shopify!
    c = (1..(ShopifyAPI::Collect.count.to_f/250.0).ceil).flat_map do |page|
      ShopifyAPI::Collect.find(:all, :params => {:page => page.to_i, :limit => 150})
    end
    c.each do |collect|
      next if Collect.find_by(shopify_collect_id: collect.id)
      product = Product.find_by(shopify_product_id: collect.product_id.to_s)
      collection = Collection.find_by(shopify_collection_id: collect.collection_id.to_s)
      col = Collect.new(shopify_collect_id: collect.id,
                        position: collect.position,
                        product_id: product.id,
                        collection_id: collection.id)  
      col.save
      puts col.errors.inspect if col.errors.any?
    end
  end

模型

  create_table "collections", force: :cascade do |t|
    t.string   "title"
    t.string   "shopify_collection_id"
    t.string   "collection_type"
    t.datetime "created_at",            null: false
    t.datetime "updated_at",            null: false
    t.integer  "shop_id"
  end

  create_table "collects", force: :cascade do |t|
    t.string   "position"
    t.string   "shopify_collect_id"
    t.datetime "created_at",         null: false
    t.datetime "updated_at",         null: false
    t.integer  "collection_id"
    t.integer  "product_id"
  end

  create_table "products", force: :cascade do |t|
    t.string   "title"
    t.string   "shopify_product_id"
    t.string   "product_type"
    t.string   "tags"
    t.datetime "created_at",         null: false
    t.datetime "updated_at",         null: false
    t.integer  "shop_id"
    t.string   "main_image_src"
  end

1 个答案:

答案 0 :(得分:2)

在您的情况下避免N + 1的更好方法是批量获取收集,产品和集合。请尝试以下代码:

def seed_collects!
  shop = self.with_shopify!
  c = (1..(ShopifyAPI::Collect.count.to_f/250.0).ceil).flat_map do |page|
    ShopifyAPI::Collect.find(:all, :params => {:page => page.to_i, :limit => 150})
  end

  shopify_collect_ids    = c.map(&:id)
  shopify_product_ids    = c.map(&:product_id)
  shopify_collection_ids = c.map(&:collection_id)

  collects    = Collect.where(shopify_collect_id: shopify_collect_ids).pluck(:shopify_collect_id, :id)
  collect_ids = Hash[collects]

  products    = Product.where(shopify_product_id: shopify_product_ids).pluck(:shopify_product_id, :id)
  product_ids = Hash[products]

  collections    = Collection.where(shopify_collection_id: shopify_collection_ids).pluck(:shopify_collection_ids, :id)
  collection_ids = Hash[collections]

  c.each do |collect|
    next if collect_ids[collect.id]
    col = Collect.new(shopify_collect_id: collect.id,
                      position: collect.position,
                      product_id: product_ids[collect.product_id.to_s],
                      collection_id: collection_ids[collect.collection_id.to_s])  
    col.save
    puts col.errors.inspect if col.errors.any?
  end
end