Rails 关联不存在。更好的方法?

Posted

技术标签:

【中文标题】Rails 关联不存在。更好的方法?【英文标题】:Rails associations NOT EXISTS. Better way? [duplicate] 【发布时间】:2012-11-09 21:18:40 【问题描述】:

使用 Rails 3.2.9 我正在尝试获取与没有所有者的组织相关联的项目列表。

我能够使用下面的方法获得一个数组列表,但对我来说似乎很难看。有没有更好的方法来做到这一点?

Items.all(:select => "items.id, items.name",
  :joins => "INNER JOIN organizations on items.organization_id = organizations.id",
  :conditions => "NOT EXISTS (select * from items k JOIN items_owners on items.id = items_owners.item_id) and items.organization_id = 1")

表设置: 业主:

身份证 姓名

项目:

身份证 姓名 organization_id

items_owners:

owner_id item_id

组织:

身份证 列表项

型号:

class Organization < ActiveRecord::Base
   attr_accessible :name

   has_many :items
end

class Item < ActiveRecord::Base
   attr_accessible :description, :name, :owner_ids, :organization_id

   has_many :items_owner
   has_many :owners, :through => :items_owner
   belongs_to :organization
end

class Owner < ActiveRecord::Base
   attr_accessible :name

   has_many :items_owner
   has_many :items, :through => :items_owner
end

class ItemsOwner < ActiveRecord::Base
   attr_accessible :owner_id, :item_id

   belongs_to :item
   belongs_to :owner
end

【问题讨论】:

我认为一个Item应该belongs_to :organization,而Organization has_many :items,会更有意义 Corrected 与另一个项目混淆了。 【参考方案1】:

编辑:删除 .all,添加 references 并添加使用 arel_table

Items.joins(:organization).includes(:owners).references(:owners).
  where('owners.id IS NULL')

如果你想同时使用includes

Items.includes(:organization, :owners).references(:organization, :owners).
  where('organisations.id IS NOT NULL AND owners.id IS NULL')

正如@Dario Barrionuevo 所写,它应该是belongs_to :organisation in Item

在第一个示例中使用arel_table

Items.joins(:organization).includes(:owners).references(:owners).
  where(Owner.arel_table[:id].eq(nil))

在 Rails 5 中(来自@aNoble 的评论):

Items.joins(:organization).left_joins(:owners).
  where(Owner.arel_table[:id].eq(nil))

但如果要在代码中引用关系,则使用includes 仍然是更可取的,以避免额外的读取。

【讨论】:

这两种方法都有效,但我必须全部删除。 SQL 输出看起来很疯狂,但它确实有效。谢谢 您的意思是在两个建议中都删除“.all”?在这种情况下,我最好改变我的建议。 在 Rails 上的从不版本(我正在测试 5.2)中,您应该使用 left_joins 而不是 includes 谢谢@aNoble,我已经更新了我的答案。但在某些情况下,最好使用包含(也许在 Rails 5 中有另一种方法?)。【参考方案2】:

在 Rails 5、6 中有很多方法可以做到 NOT EXISTS:

    不同的项目 OUTER JOIN item_owners 其中 item_owners.id 为空 items.id 不在(从 item_owners 中选择 item_id) 不存在(从 item_owners 中选择 1 个,其中 item_id = items.id) where (select COUNT(*) from item_owners where item_id = items.id) = 0

我能想到 4 种方法,但我似乎记得有 7 种方法。无论如何,这是一个切线,但可能会给您一些更适合您的用例的想法。

我发现使用 NOT IN 方法对我的团队来说是最容易创建和维护的。 我们的目标是避免 arel,支持 owner 表中的 WHERE 子句(例如:admin owner),并支持多个级别的 rails :through。

Items.where.not(id: Items.joins(:owners).select(:id))
     .select(:id, :name)

Items.where.not(id: Items.joins(:items_owners).select(:id))
     .select(:id, :name)

Items.where.not(id: ItemOwners.select(:item_id))

我们使用第一个,但这些示例应该按照从最优化到最佳的顺序排列。同样按照对模型的了解最少到最多的顺序。

【讨论】:

【参考方案3】:

试试这个

Items.joins(:organisations).where(Items.joins(:items_owners).exists.not).select('items.id,items.name')

【讨论】:

这很接近。下面是 SQL 输出。问题是最后一个连接的范围是使用当前选择中的 items.id 而不是顶部选择。 SELECT items.id,items.name FROM "items" INNER JOIN "organizations" ON "organizations"."id" = "items"."organization_id" WHERE (NOT (EXISTS (SELECT "items".* FROM "items" INNER加入 "items_owners" ON "items_owners"."item_id" = "items"."id")))

以上是关于Rails 关联不存在。更好的方法?的主要内容,如果未能解决你的问题,请参考以下文章

Rails - 验证关联的存在?

查找所有关联断开的对象

在 Rails 中,更新记录或创建新记录(如果不存在)的最佳方法是啥?

rails 关联:用户必须存在问题

Rails 3:通过关联项验证是不是存在至少一个有多个

列出存在于另一个模型中的所有关联模型记录,该模型存在于 rails 中的另一个命名空间中