在查找关联记录时减少查询

时间:2013-04-09 05:40:09

标签: sql ruby-on-rails

我有这个代码,它执行了大量的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

我在最多的方法中有大量的查询,我想知道,如果我能以某种方式减少这些问题?

编辑:

我看到的主要问题是大多数相关的 - 它执行了大量查询,见下文:

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]]

2 个答案:

答案 0 :(得分:0)

我不确定没有看到你的应用正在进行的SQL查询,但我怀疑它在这部分代码中遇到了 N + 1查询问题

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

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

在上面的代码中可能还有其他实例,但一般来说,N + 1查询问题的解决方案是在从DB中获取“父”模型时使用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

答案 1 :(得分:0)

尝试划分句子

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

分为两个,一个用于所有项目的ID,另一个用于所有类别,通过项目和类别之间的表格,如

1. Item.includes(:categories).where("categories.id IN (?)", self.purchased_categories)
2. Category.includes(:items).where("items.id IN (?)", <THE ids of Items>)