【问题标题】:Finding nil has_one associations in where query在 where 查询中查找 nil has_one 关联
【发布时间】:2012-09-20 14:51:45
【问题描述】:

这可能是一个简单的问题,但我似乎很想在这里找到一个优雅的解决方案。我有两个 ActiveRecord 模型类,它们之间有一个 has_one 和 belongs_to 关联:

class Item < ActiveRecord::Base
  has_one :purchase
end

class Purchase < ActiveRecord::Base
  belongs_to :item
end

我正在寻找一种优雅的方式来查找所有没有与之关联的购买对象的 Item 对象,最好不要在 Item 上使用布尔值 is_purchased 或类似属性。

现在我有:

purchases = Purchase.all
Item.where('id not in (?)', purchases.map(&:item_id))

这很有效,但对我来说似乎效率低下,因为它正在执行两个查询(并且购买可能是一个庞大的记录集)。

运行 Rails 3.1.0

【问题讨论】:

    标签: ruby-on-rails ruby activerecord


    【解决方案1】:

    这是很常见的任务,SQL OUTER JOIN 通常可以正常工作。以here 为例。

    在你的情况下尝试使用类似的东西

    not_purchased_items = Item.joins("LEFT OUTER JOIN purchases ON purchases.item_id = items.id").where("purchases.id IS null")
    

    【讨论】:

    • 那个链接是完美的!加入几乎正是我所需要的,我只需要用where("purchases.item_id IS null") 替换上面的位置就可以了。谢谢!
    • 如果使用has_manyhas_one 可以实现连接,而无需使用Item.join(:purchases) 编写实际查询
    【解决方案2】:

    发现了另外两种 railsey 方法:

    Item.includes(:purchase).references(:purchase).where("purchases.id IS NULL")
    
    Item.includes(:purchase).where(purchases: { id: nil })
    

    从技术上讲,第一个示例在没有 'references' 子句的情况下工作,但 Rails 4 在没有它的情况下会发出弃用警告。

    【讨论】:

    • 虽然这比接受的答案更简洁,但它会从关联表中加载所有数据。如果您想让 SQL 查询保持整洁,请改用 @dimuch 建议的自定义联接。
    • @deadwards 我认为这不是真的。 Asset.includes(:attachments).where(attachments: { id: nil}) 产生:SELECT "assets"."property_id" AS t0_r0, ... "attachments"."updated_at" AS t1_r7 FROM "assets" LEFT OUTER JOIN "attachments" ON "attachments"."property_id" = "assets"."property_id" WHERE "attachments"."id" IS NULL,与@dimuch 的示例相同。
    • Rails 4 路:Item.includes(:purchases).references(:purchases).where(purchases: { id: nil })。如果你在它上面调用 to_sql,你会发现它肯定不会加载所有数据。它与接受的答案完全相同。
    • @bronson - 应该是:Item.includes(:purchase).references(:purchases).where(purchases: { id: nil }); includes(:purchase) 上没有“s”
    【解决方案3】:

    更简洁的@dimuch 解决方案是使用Rails 5 中引入的left_outer_joins 方法:

    Item.left_outer_joins(:purchase).where(purchases: {id: nil})
    

    请注意,在left_outer_joins 调用中:purchase 是单数(它是由has_one 声明创建的方法的名称),而在where 子句中:purchases 是复数(这里是id 字段所属的表的名称。)

    【讨论】:

    • 你能指定 Rails 版本吗?我相信这是 > 5。
    【解决方案4】:

    Rails 6.1 在ActiveRecord::QueryMethods::WhereChain 类中添加了一个名为missing 的查询方法。

    它返回一个带有左外连接的新关系以及父模型和子模型之间的 where 子句,以识别缺失的关系。

    例子:

    Item.where.missing(:purchase)
    

    【讨论】:

      猜你喜欢
      • 2017-07-11
      • 1970-01-01
      • 1970-01-01
      • 2016-12-19
      • 1970-01-01
      • 2017-09-07
      • 2011-03-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多