用`where.missing`查找缺少关联的记录

110 阅读1分钟

你不能证明否定的东西,但是查询数据库中的否定的东西呢?虽然大多数时候你写查询是为了寻找数据,但在有些情况下,你想要的是相反的:写一个寻找没有数据的查询。

当涉及到原始SQL时,你可以使用LEFT OUTER JOIN ,结合NULL 检查来寻找没有某些关联的记录。

使用方法

在Rails中,你可以直接用ActiveRecord来应用SQL的相同概念。

比方说,你有以下模型:

class Account < ApplicationRecord
  has_many :recovery_email_addresses
end

如果你想找到还没有设置备份恢复邮件的Account,你可以写这个查询,一切都会很顺利:

Account.left_joins(:recovery_email_addresses).where(recovery_email_addresses: { id: nil })

# SELECT "accounts".* FROM "accounts" LEFT OUTER JOIN "recovery_email_addresses" ON "recovery_email_addresses"."account_id" = "accounts"."id" WHERE "recovery_email_addresses"."id" IS NULL

但这有点啰嗦。从Rails 6.1开始,你可以用一个更简洁的速记方法来写这个相同的查询:

Account.where.missing(:recovery_email_addresses)

# SELECT "accounts".* FROM "accounts" LEFT OUTER JOIN "recovery_email_addresses" ON "recovery_email_addresses"."account_id" = "accounts"."id" WHERE "recovery_email_addresses"."id" IS NULL

这将产生相同的SQL,而且更容易阅读。你也可以在belongs_to 关系上使用这个功能:

class Contract < ApplicationRecord
  belongs_to :promiser, class_name: "User"
  belongs_to :promisee, class_name: "User"
  belongs_to :beneficiary, optional: true, class_name: "User"
end

Contract.where.missing(:promiser) # Contracts without a promiser
Contract.where.missing(:promiser, :beneficiary) # Contracts without a promiser AND beneficiary

你还可以将missing 与你正常的ActiveRecord链式方法结合起来:

Contact.where("amount > ?", 1200).where.missing(:promiser)
Contact.where(signed: true).where.missing(:beneficiary)