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")
瞧。
我有一个模型与另一个模型有 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")
瞧。