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

尝试分割句子

Item.includes(:categories).where("categories.id IN (?)", self.purchased_categories).references(:categories).uniq

一分为二,一个用于Items的所有id,另一个用于通过Item和Category之间的表的所有类别,比如

1. Item.includes(:categories).where("categories.id IN (?)", self.purchased_categories)
2. Category.includes(:items).where("items.id IN (?)", <THE ids of Items>)
于 2013-04-09T05:56:02.917 回答
0

如果没有看到您的应用程序正在执行的确切 SQL 查询,我不确定,但我怀疑它在这部分代码中遇到了N + 1 查询问题:

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

这将为每个项目执行单独的查询以获取其关联的类别(这就是您看到这么多SELECT "categories"."id" ...查询的原因)。但是在两个查询中选择所有项目的类别会更有效。

在上面的代码中可能还有其他实例,但通常 N + 1 查询问题的解决方案是在includes(...)从数据库中获取“父”模型时使用。阅读有关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 回答