Rails关联不存在.更好的方式



使用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")

表设置:
所有者:

  • id
  • 名称

项目:

  • id
  • 名称
  • organization_id

items_owners:

  • 所有者id
  • item_id

组织:

  • 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
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所写,它应该是Item中的belongs_to :organisation

在第一个示例中使用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仍然是优选的,以避免额外的读取。

有很多方法可以在轨道5、6中不存在:

  1. 不同项OUTER JOINitem_owners.id为null的item_owners
  2. items.idNOT IN(从item_owners中选择item_id)
  3. 不存在(从item_owners中选择1,其中item_id=items.id)
  4. 其中(从item_owners中选择COUNT(*),其中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))

我们使用第一个,但这些示例应该按照从优化程度最低到优化程度最高的顺序排列。也按知识从最少到最多的顺序排列模型。

试试这个

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

最新更新