ruby-on-rails 如何在rails中使用where语句检查空关联?

bihw5rsg  于 2023-10-21  发布在  Ruby
关注(0)|答案(5)|浏览(143)

我有一个名为Category的模型,它可以有许多子类别(也是Category记录)。我想收集数据库中没有子类别的所有类别,如下所示:

Category.where(subcategories: [])

但我所尝试的每一种直觉的变化要么抛出错误,要么返回空集。它生成了一些我不太理解的SQL:

Category Load (0.5ms) SELECT "categories".* FROM "categories" WHERE 1=0
=> #<ActiveRecord::Relation []>

有谁能告诉我正确的方法吗?
编辑:这是定义子类别关系的方式

class Category < ActiveRecord::Base
    has_many :subcategories, class_name: 'Category', foreign_key: 'parent_id'
    belongs_to :parent, class_name: 'Category'
end
1rhkuytd

1rhkuytd1#

试试这个:

Category.joins(:subcategories).where(subcategories: {parent_id: nil})

Category.includes(:subcategories).where('subcategories.parent_id IS NULL')
epggiuax

epggiuax2#

好了,解决了。我只需要调整Sharvy的建议(我忘记了一件大事,就是添加一个“joins”)。
以下方法起作用:

Category.includes(:subcategories).where(subcategories: nil)

**更新2:**好吧,这次我真的让它工作了(很确定,至少...)。我不得不使用一些丑陋的SQL,而不仅仅是一些漂亮的ActiveRecord语法,但下面的代码似乎可以满足我的需求:

Category.joins("LEFT JOIN categories AS subcategories ON subcategories.parent_id = categories.id").where("subcategories IS NULL")

**更新1:**对不起,没关系,这不起作用。正如我在下面评论的那样,我跳枪了,因为我看到这个查询返回了where(... nil)where.not(... nil)的正确类别计数,但它实际上没有返回正确的类别。它返回所有没有父类别的类别,而不是所有没有子类别的类别。

以下是此查询的一些示例输出:

Category.includes(:subcategories).where(subcategories: nil).last
  Category Load (0.7ms)  SELECT  "categories".* FROM "categories" WHERE "categories"."parent_id" IS NULL  ORDER BY "categories"."id" DESC LIMIT 1
  Category Load (0.5ms)  SELECT "categories".* FROM "categories" WHERE "categories"."parent_id" IN (3158)
 => #<Category id: 3158, name: "A parent", parent_id: nil, created_at: "2015-03-23 19:18:40", updated_at: "2015-03-23 19:18:40", operator_id: nil>
oalqel3c

oalqel3c3#

除了公认的答案,我喜欢这个:

Category.joins("LEFT JOIN categories as subcategories 
                    ON subcategories.parent_id = categories.id")
            .having("COUNT(subcategories.id) = 0")

这是一种更通用的方法(显然效率不高),因为它可以处理任何数量的表,而不仅仅是两个表。
例如,您有一个orders表,它有许多line_items,还有一个subscriptions表,它属于一个line_item。要检查哪些订单不包含订阅,请执行以下操作:

orders.joins('INNER JOIN line_items 
                ON line_items.order_id = orders.id 
                LEFT JOIN subscriptions
                ON subscriptions.line_item_id = line_items.id')
         .having("COUNT(subscriptions.id) = 0").group(:id)
v2g6jxz6

v2g6jxz64#

Rails 6.1+添加了一个方法,使其更容易:

Category.where.missing(:subcategories)
brvekthn

brvekthn5#

怎么样

Category.where("id NOT IN (?)", Subcategory.pluck(:category_id))

这里的问题是您不能直接在.where子句中查询关联。您只能处理实际的列名。所以我在这里做了一个快速的子查询,在这里您可以获取Subcategory表中作为关联列出的类别ID,然后查询不在该列表中的所有类别。

相关问题