在我的系统中,我有以下结构:
class Worker
has_many :worker_memberships
end
class WorkerMembership
belongs_to :worker
belongs_to :event
end
class Event
has_many :worker_memberships
end
想象一下,我有一个@event
。如何找到所有workers
属于此worker_memberships
的{{1}}?
答案 0 :(得分:2)
这几乎是其他答案的综合。
首先:坚持has_many through
,因为@TheChamp建议。你可能已经在使用它,只是忘了写它,否则它就不会工作了。好吧,你已被警告过了。
我通常尽力避免在我的查询中使用原始SQL。我在上面提供的关于select
的提示产生了一个可行的解决方案,但是在没有实际需要时会做一些不必要的事情,例如join
。所以,让我们避免戳一个关联。不是这次。
以下是我在多对多关联中更喜欢has_many through
到has_and_belongs_to_many
的原因:我们可以在没有原始SQL的情况下查询连接模型本身:
WorkerMembership.select(:worker_id).where(event: @event)
这还不是结果,但它会让我们得到我们不想要的worker_id
个列表。然后我们将这个查询包装成一个"除了这些家伙之外,给我所有的":
Worker.where.not(id: <...> )
所以最后的查询是:
Worker.where.not(id: WorkerMembership.select(:worker_id).where(event: @event) )
并输出单个查询(@event
上的id
等于1
):
SELECT `workers`.* FROM `workers` WHERE (`workers`.`id` NOT IN (SELECT `worker_memberships`.`worker_id` FROM `worker_memberships` WHERE `worker_memberships`.`event_id` = 1))
我还赞赏@apneadiving的解决方案和关于mysql2
explain
的提示。 SQLite的explain
太可怕了!如果我正确阅读explain
的结果,我的解决方案就像@ apneadiving一样高效。
@TheChamp还为所有答案提供了性能成本&#39;查询。查看评论以进行比较。
答案 1 :(得分:1)
试试这个:
Worker.where(WorkerMembership.where("workers.id = worker_memberships.worker_id").where("worker_memberships.event_i = ?", @event.id).exists.not)
或更短且可重复使用:
class WorkerMembership
belongs_to :worker
belongs_to :event
scope :event, ->(event){ where(event_id: event.id) }
end
Worker.where(WorkerMembership.where("workers.id = worker_memberships.worker_id").event(@event.id).exists.not)
(我假设公约中的表名和列名)
答案 2 :(得分:1)
由于您要在Worker
和Event
之间设置多对多的关系,我建议您使用through association。
您的结果模型将是。
class Worker
has_many :worker_memberships
has_many :events, :through => :worker_memberships
end
class WorkerMembership
belongs_to :worker
belongs_to :event
end
class Event
has_many :worker_memberships
has_many :workers, :through => :worker_memberships
end
现在,您只需致电@event.workers
即可获得与该活动相关联的所有工作人员。
要查找不属于您可以使用的@event
的所有工作人员:
# get all the id's of workers associated to the event
@worker_ids = @event.workers.select(:id)
# get all workers except the ones belonging to the event
Worker.where.not(:id => @worker_ids)
单线
Worker.where.not(:id => @event.workers.select(:id))