如何在 Rails 中使用自引用 table 避免 N + 1 次查询

How to avoid N + 1 queries with self-referential table in Rails

如何正确使用 includes 以避免在这种情况下进行 N + 1 次查询:

我有一组可以嵌套的类别(即它们形成一棵树)。例如:

要设置此层次结构,每个 Category 记录都有 parent_id 作为外键。

这是我的 Rails 型号:

class Category < ApplicationRecord
  belongs_to :user
  belongs_to :parent, class_name: "Category", optional: true
  has_many :children, class_name: "Category", foreign_key: "parent_id"
end

我使用

访问给定用户的所有类别
@categories = Category.includes(:children).where(user_id: user.id)

但是,每次调用 @categories[i].children 都会生成一个新查询。

如何正确使用 includes 以便我可以访问每个类别的子类别而无需额外查询。

(我也尝试过 @categories = Category.where(user_id: user.id).includes(:children),但行为没有任何变化。)

includes 通常用于预先加载另一个 table。在这种情况下,您已经可以访问正在搜索的 table categories.

你可以做的是构建一个 Hash/Dictionary 类别,这需要调用你的 categories table 来生成哈希。

category_hash = {}
Category.where(user_id: user.id).each do |category|
  category_hash[category.id] = {}
  # put whatever data you want to reference here
  category_hash[category.id][:parent_id] = category.parent_id
  category_hash[category.id][:name] = category.name
end

然后在将您的类别引用为 category_hash[:some_id] 时将为您提供您想要存储在哈希中的任何数据...使用 O(1) 时间并且没有额外的数据库查询。

你也必须使用 joins,所以你可以使用 categories 和所谓的 children_categories:

之间的关系
Category.includes(:children).joins(:children).where(user_id: <user_id>)
# SELECT "categories"."id" AS t0_r0,
#        ...
#        "children_categories"."id" AS t1_r0,
#        ...
# FROM "categories"
# INNER JOIN "categories" "children_categories"
# ON "children_categories"."parent_id" = "categories"."id"
# WHERE "categories"."user_id" = 

否则你会看到我认为你现在的问题是什么:

Category.includes(:children).where(user_id: <user_id>)
# SELECT "categories".* FROM "categories" WHERE "categories"."user_id" = 
# SELECT "categories".* FROM "categories" WHERE "categories"."parent_id" IN (, , ...)