1

私はこのコードを持っています。これは、特に最後のメソッド (「最も関連する」) を使用して、非常に多くの SQL クエリを実行しています。私の次のセットアップはこれです:

class User < ActiveRecord::Base
  authenticates_with_sorcery!
  has_and_belongs_to_many :items


  def purchased_categories
    ids = []
    self.items.each do |item|
      ids << item.categories.pluck(:id)
    end
    ids.flatten.uniq
  end

  def recommended_items
    Item.includes(:categories).where("categories.id IN (?)",       self.purchased_categories).references(:categories).uniq - self.items
  end

  def most_related
    cs = self.purchased_categories
    self.recommended_items.sort { |a, b| (a.categories.pluck(:id) & cs).length <=> (b.categories.pluck(:id) & cs).length }
  end

end

私のアイテムモデルは次のようになります。

class Item < ActiveRecord::Base
  has_and_belongs_to_many :categories
  has_and_belongs_to_many :users
end

most_related メソッドに大量のクエリがあり、どうにかしてそれらを減らすことができるかどうか疑問に思っていました。

編集:

私が見ている主な問題は、most_related にあります - 大量のクエリを実行しています。以下を参照してください。

Item Load (4.1ms)  SELECT "items".* FROM "items" INNER JOIN "items_users" ON "items"."id" = "items_users"."item_id" WHERE "items_users"."user_id" = $1  [["user_id", 815249]]
   (0.9ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1253]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1253]]
  SQL (2.9ms)  SELECT DISTINCT "items"."id" AS t0_r0, "items"."name" AS t0_r1, "items"."created_at" AS t0_r2, "items"."updated_at" AS t0_r3, "categories"."id" AS t1_r0, "categories"."name" AS t1_r1, "categories"."created_at" AS t1_r2, "categories"."updated_at" AS t1_r3 FROM "items" LEFT OUTER JOIN "categories_items" ON "categories_items"."item_id" = "items"."id" LEFT OUTER JOIN "categories" ON "categories"."id" = "categories_items"."category_id" WHERE (categories.id IN (134,152))
   (0.8ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1684]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1596]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1596]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1684]]
   (0.4ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1622]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1685]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.8ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1683]]
   (0.7ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.7ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1378]]
   (0.7ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1594]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.4ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1678]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1428]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1427]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1623]]
   (0.4ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1676]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1456]]
   (0.7ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1532]]
   (1.1ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1546]]
   (0.4ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1641]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1681]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.7ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1677]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.8ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1288]]
   (0.7ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1533]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1686]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1643]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1679]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1682]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.8ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1687]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1675]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1376]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1549]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1680]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1750]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1643]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1623]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1623]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1596]]
   (0.7ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1533]]
   (0.7ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1623]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1532]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1623]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1378]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1623]]
   (0.5ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1622]]
   (0.6ms)  SELECT "categories"."id" FROM "categories" INNER JOIN "categories_items" ON "categories"."id" = "categories_items"."category_id" WHERE "categories_items"."item_id" = $1  [["item_id", 1623]]
4

2 に答える 2

0

アプリが作成している正確な SQL クエリを見ないとわかりませんが、このコード セクションのN + 1 クエリの問題に苦しんでいると思われます。

self.items.each do |item|
  ids << item.categories.pluck(:id)
end

これにより、アイテムごとに個別のクエリが実行され、関連するカテゴリが取得されます (これが、非常に多くのSELECT "categories"."id" ...クエリが表示される理由です)。ただし、2 つのクエリだけでカテゴリのすべてのアイテムを選択する方が効率的です。

上記のコード内にこれの他のインスタンスがあるかもしれませんが、一般に、N + 1 クエリの問題に対する解決策はincludes(...)、DB から「親」モデルを取得するときに使用することです。詳細については、Eager Loading Associationsに関する Rails ガイドのセクションを参照してください。

編集has_many :through:これらのモデル関係の代わりに使用する方が効率的でhas_and_belongs_to_manyあり、クエリも簡単にすることで問題を解決するのに役立つ場合があります。に切り替えた場合has_many :through、次のようなものになります(はるかに簡単なpurchased_categories方法で):

class User
  has_many :items
  has_many :categories, :through => :items

  def purchased_categories
    category_ids
  end
end

class Item
  belongs_to :user
  belongs_to :category
end
于 2013-04-09T05:49:52.770 に答える