40

I found one answer that had some usable having examples for finding parents with n children, but the same is not usable for finding parents with no children (presumably since the join excludes them).

scope :with_children, joins(:children).group("child_join_table.parent_id").having("count(child_join_table.parent_id) > 0")

Can anyone point me in the right direction?

4

3 回答 3

77

更新 Rails 6.1

使用新的 Rails 版本,这变得很简单,如下所述

.where.missing(:children)

对于旧版本,请参见下文。

导轨 3 和 4

scope :without_children, includes(:children).where(:children => { :id => nil })

这里最大的区别是joins成为一个includes:一个包含加载所有关系,如果它们存在,则连接将只加载关联的对象并忽略没有关系的对象。

实际上,scope :with_children, joins(:children)应该足以返回至少有 1 个孩子的父母。试试看!


导轨 5

请参阅下面的@Anson 的答案


宝石activerecord_where_assoc

activerecord_where_assocgem 可以为 Rails 4.1 到 6.0 执行此操作。

scope :without_children, where_assoc_not_exists(:children)

自引用关系被无缝处理。

这也避免了诸如joins使查询为单个记录返回多行等问题。


正如@MauroDias 指出的那样,如果它是您的父母和孩子之间的自我参照关系,那么上面的代码将不起作用。

通过一些研究,我发现了如何做到这一点:

考虑这个模型:

class Item < ActiveRecord::Base
  has_many :children, :class_name => 'Item', :foreign_key => 'parent_id'

如何退回所有没有孩子的物品:

Item.includes(:children).where(children_items: { id: nil })

我是怎么找到那张children_items桌子的?

Item.joins(:children)生成以下 SQL:

SELECT "items".* 
FROM "items" 
 INNER JOIN "items" "children_items" 
 ON "children_items"."parent_id" = "items"."id"

所以我猜想 Rails 在自引用情况下需要 JOIN 时会使用表。


类似的问题:

于 2013-08-06T13:51:56.433 回答
31

@MrYoshiji 有一个可靠的 Rails 4 答案,但对于使用 Rails 5 来到这里的人来说,你有更多选择。

使用 Rails 5:

从 Rails 5 开始,您还可以使用left_outer_joins来避免加载关联。它是在拉取请求#12071中引入的。

scope :without_children, -> { left_outer_joins(:children).where(children: { id: nil }) }

对于有孩子的父母,MrYoshiji 的 Rails 4 解决方案仍然是一个可以使用的解决方案:

scope :with_children, -> { joins(:children).uniq }
于 2016-11-09T16:04:03.133 回答
4

这就是我为 Rails 5 解决的方法:

scope :without_comments, -> do
  left_outer_joins(:comments).where(comments: { id: nil })
end
于 2018-05-29T12:02:41.053 回答