繁体   English   中英

多对多自连接表上的 ActiveRecord 查询

[英]ActiveRecord query on many-to-many self join table

我有一个名为people的多对多自连接表,它使用以下模型:

class Person < ApplicationRecord
  has_and_belongs_to_many :children,
    class_name: "Person",
    join_table: "children_parents",
    foreign_key: "parent_id",
    association_foreign_key: "child_id",
    optional: true

  has_and_belongs_to_many :parents,
    class_name: "Person",
    join_table: "children_parents",
    foreign_key: "child_id",
    association_foreign_key: "parent_id",
    optional: true
end

如果在上面的模型中不明显 - 除了数据库中的people表,还有一个children_parents连接表,其中包含两个外键索引字段child_idparent_id 这使我们能够表示孩子和父母之间的多对多关系。

我想查询一个人的兄弟姐妹,所以我在 Person 模型中添加了以下方法:

def siblings
  self.parents.map do |parent|
    parent.children.reject { |child| child.id == self.id }
  end.flatten.uniq
end

但是,这会生成三个 SQL 查询:

  Person Load (1.0ms)  SELECT "people".* FROM "people" INNER JOIN "children_parents" ON "people"."id" = "children_parents"."parent_id" WHERE "children_parents"."child_id" = $1  [["child_id", 3]]
  Person Load (0.4ms)  SELECT "people".* FROM "people" INNER JOIN "children_parents" ON "people"."id" = "children_parents"."child_id" WHERE "children_parents"."parent_id" = $1  [["parent_id", 1]]
  Person Load (0.4ms)  SELECT "people".* FROM "people" INNER JOIN "children_parents" ON "people"."id" = "children_parents"."child_id" WHERE "children_parents"."parent_id" = $1  [["parent_id", 2]]

我知道可以将其设为单个 SQL 查询,如下所示:

SELECT DISTINCT(p.*) FROM people p
INNER JOIN children_parents cp ON p.id = cp.child_id
WHERE cp.parent_id IN ($1, $2)
AND cp.child_id != $3

$1$2是此人的父 ID, $3是此人的 ID。

有没有办法使用 ActiveRecord 执行此查询?

你可以使用这样的东西:

def siblings
  Person.select('siblings.*').from('people AS siblings').where.not(id: id)
    .where(
      parents.joins(
        'JOIN children_parents ON parent_id = people.id AND child_id = siblings.id'
      ).exists
    )
end

在这里你可以看到一些奇怪的东西:

设置表别名。 你应该避免这种情况,因为在这样的表别名后,活动记录对来自 ruby​​ 的列名不再有帮助: where(column: value).order(:column) - 将不起作用,只剩下普通的 sql 字符串

存在- 我经常使用它而不是连接。 当您将多条记录合并为一条记录时,您会收到重复的记录,然后出现不同的分组的新问题。 Exists还提供了查询的隔离:EXISTS 表达式中的表和列对于查询的其他部分是不可见的。 在 rails 中使用它的不好的部分:至少需要 1 个普通 SQL。

这种方法的一个弱点是:如果你在某处为每条记录调用它,那么每条记录都会有 1 个查询 - N+1 问题。

现在关于 Rails Way 的几句话。 Rails 指南建议始终使用 has_many :through 而不是 habtm,我在这里看到它: https : //github.com/rubocop-hq/rails-style-guide

我理解的 Rails 意识形态代表着开发速度和维护的简单性。 首先意味着性能无关紧要(想象一下您需要多少用户开始使用它),其次说普通 SQL 的灵活性很好,但在 rails 中不行,在 rails 中请使代码尽可能简单(请参阅 rubocop默认值:方法中的 10 loc,类中的 100 loc,以及 4 个总是说您的代码太复杂的复杂性指标)。 我的意思是,许多现实世界的 Rails 项目都使用 N+1 进行查询,进行无效查询,这很少成为问题

因此,在这种情况下,我会建议尝试includespreloadeager_load

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM