问题:使用 ActiveRecord 助手删除 VS 查找孤儿

我正在尝试删除不再有任何users的所有organizations

使用下面的代码,我可以找到我想要删除的所有记录:

Organization.includes(:users)
  .where(users: { id: nil })
  .references(:users)

当我添加delete_all时,如果我不包含references,我会得到同样的错误:

PG::UndefinedTable: ERROR:  missing FROM-clause entry for table "users"

我可能可以用纯 SQL 编写解决方案,但我不明白为什么当我添加delete_all语句时 Rails 没有保留对users的引用。

以下是更多细节:

Organization:
  has_many :users

User:
  belongs_to :organization

解答

我发现includes仅对急切加载有用(它可以很少处理我的情况),并且当与references结合使用时,它会生成完全疯狂的东西(用tN_rM之类的东西为每个字段别名)甚至虽然它实际上做了一个LEFT OUTER JOIN...如果它没有在delete_all出现后消失,那它_可以_帮助!

我发现仅仅使用exists就更清晰、更简单了。它是 Arel(避免它是没有意义的,它无论如何都在 ActiveRecord 的引擎盖下),但它是如此之小,以至于它几乎不引人注意:

Organization.where(
  User.where('users.organization_id = organizations.id').exists.not
)

或者,如果这个 SQL 字符串对您来说不好看,请使用更多的 Arel,这样它就会变得明显:

Organization.where(
  User.where(organization_id: Organization.arel_table[:id]).exists.not
) # I tend to extract these   ^^^^^^^^^^^^^^^^^^^^^^^ into local variables

这可以很好地处理将.delete_all链接到顶部,因为它不是(在语法上)连接,即使它实际上等效于一个。

这背后的魔力

SQL 有一个EXISTS运算符,它在功能上类似于连接,除了无法从连接表中选择字段。它形成一个有效的布尔表达式,可以否定扔到WHERE-conditions 中。

在“无 SQL”形式中,我使用了一个表达式“表的列”,结果证明它可以在 Rails 的哈希条件中使用。这是一个偶然的发现,是 Arel 为数不多的不会使代码变得过于庞大的用途之一。

Logo

PostgreSQL社区为您提供最前沿的新闻资讯和知识内容

更多推荐