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_iditems_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 关联不存在。更好的方法?的主要内容,如果未能解决你的问题,请参考以下文章