Rails - 仅查找存在 has_many 关联记录的记录

Rails - only find records where has_many associated records are present

我有一个模型与另一个模型有 has_many 关系,如下所示:

class Parent < ActiveRecord::Base
  has_many :children
end

class Child < ActiveRecord::Base
  belongs_to :parent
end

因为有些 parents 可能没有 children,我想查询 returns 只有 parents 确实有 children。我该如何处理这样的事情?

由于我是在 Rails 3 上执行此操作,因此如果此查询不使用 where.not. 语法会很有帮助。

可以使用 SQL 语法完成

Parent.where('id IN (SELECT DISTINCT(parent_id) FROM children)')

或者,为了保持干燥,可以在范围内使用:

class Parent < ActiveRecord::Base
  has_many :children

  scope :with_children, where('id IN (SELECT DISTINCT(parent_id) FROM children)')
end

然后您可以使用以下方法找到具有 children 的 parents:

Parent.with_children

Rails 4

Parent.includes(:child).where.not(children: {id: nil})

Parent.joins(:child).distinct

Rails 3

Parent.joins(:child).distinct

类似于 Wes 的回答,但对 SQL 语法有点害羞:

Parent.find(Child.all.map{|c|c.parent_id}.uniq)

这位:

Child.all.map{|c|c.parent_id}.uniq

为您提供了一个 parent_ids 的数组(使用 .uniq 删除了重复项)。

从那里开始,这是一个简单的 .find。或者,如果您愿意,请使用 .where

where.associated (Rails 7+)

Rails 7 引入了一种检查是否存在关联的新方法 - where.associated.

请看下面的代码片段:

# Before:
account.users.joins(:contact).where.not(contact_id: nil)

# After:
account.users.where.associated(:contact)

这是在幕后使用的 SQL 查询示例:

Post.where.associated(:author)
# SELECT "posts".* FROM "posts"
# INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# WHERE "authors"."id" IS NOT NULL

因此,您的具体案例可以改写如下:

Parent.where.associated(:child)

谢谢。

来源:

备注:

  • where.missing - 从 Rails 6.1.
  • 开始也可以使用检查是否存在关联的对应项
  • 参见 offical docs and this answer

由于 RIGHT (OUTER) JOIN returns 来自关联(右)模型的所有记录,从(左)模型中消除不匹配的记录,它在 Rails 查询中链接时的用法将有效过滤掉所有没有关联记录的记录。

它充分利用了数据库引擎,提供最佳的执行性能,并使用单一标准、高度可移植的SQL语句:

Author.joins("RIGHT OUTER JOIN posts ON posts.author_id=authors.id")

瞧。