查找关联记录不属于某个记录的记录

时间:2014-10-31 09:49:16

标签: ruby-on-rails ruby activerecord

在我的系统中,我有以下结构:

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}}?

3 个答案:

答案 0 :(得分:2)

这几乎是其他答案的综合。

首先:坚持has_many through,因为@TheChamp建议。你可能已经在使用它,只是忘了写它,否则它就不会工作了。好吧,你已被警告过了。

我通常尽力避免在我的查询中使用原始SQL。我在上面提供的关于select的提示产生了一个可行的解决方案,但是在没有实际需要时会做一些不必要的事情,例如join。所以,让我们避免戳一个关联。不是这次。

以下是我在多对多关联中更喜欢has_many throughhas_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)

由于您要在WorkerEvent之间设置多对多的关系,我建议您使用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))