2014-10-31 56 views
3

在我的系统记录,我有以下结构:找哪家assoicated记录不属于某个记录

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属于这@event

回答

2

这既是其他的答案几乎合成。

第一:坚持has_many through @TheChamp建议。你可能已经在使用它,只是忘了写它,否则它不会工作。那么,你已经受到警告。

我一般尽我所能避免我的查询中的原始SQL。我上面提供的select提示产生了一个工作解决方案,但是会做一些不需要的东西,比如join,如果没有实际需要的话。所以,让我们避免戳一个关联。这次不行。

这里谈到为什么我在许多-to-many关联宁愿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)) 

,并将其输出单个查询(上@eventid等于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的提示。 SQLite的explain是可怕的!我的解决方案,如果我正确阅读explain的结果,就像@ apneadiving一样。

@TheChamp还为所有答案的查询提供了性能成本。查看比较评论。

+0

有趣! :) – apneadiving 2014-10-31 12:39:31

+0

你会介意分担两者的费用吗?只是好奇心 – apneadiving 2014-10-31 12:40:48

+0

@apneadiving肯定的事情:http://pastebin.com/BupHkLQf(一定要打开文字包装,'解释'表很长) – 2014-10-31 12:52:27

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) 

(我假设从公约表和列名)

+0

我建议你尝试所有的解决方案,但追加他们与'.explain'。这样你会看到数据库成本。然后,我强烈建议您选择最低的 – apneadiving 2014-10-31 10:35:16

+0

谢谢指向'explain'方法。我会对你对结果的看法感兴趣。 http://chat.stackoverflow.com/rooms/64000/explain-activerecord-db-queries – 2014-10-31 10:49:23

+0

实际上,在一个几乎为空的表上,这个查询会抱怨'nil'中缺少'first',并且导致长堆栈跟踪失败。 – 2014-10-31 10:54:17

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)) 
+0

是的,但不需要做2分贝查询,但基准不明确 – apneadiving 2014-10-31 10:17:46

+0

@apneadiving用'select'替换'pluck'将使用子查询来做到这一点。像'Worker.where.not(id:@ event.workers.select(:id))' – 2014-10-31 10:23:43

+0

@ D-side,很酷:)谢谢allot,它好多了。 – 2014-10-31 10:24:37