2014-10-31 60 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