2016-04-10 28 views
2

我的關係是Client可以有很多ClientJobs。我希望能夠找到執行Job aJob b的客戶端。我使用了3個選擇框,因此我可以選擇最多三個作業進行選擇。選擇框從數據庫填充。Rails:AND運算符在has_many關聯

我知道如何用下面的查詢測試1項工作。但我需要一種方法來使用AND運算符來測試該客戶端是否存在兩個作業。

@clients = Client.includes("client_jobs").where(
    client_jobs: { job_name: params[:job1]}) 

不幸的是很容易做到像下面的IN操作,但我想爲AND語法應該是相似的....我希望

@lients = Client.includes("client_jobs").where(
    client_jobs: { job_name: [params[:job1], params[:job2]]}) 

編輯:發佈的SQL語句從下面的答案中選擇數據庫

Core Load (0.6ms) SELECT `clients`.* FROM `clients` 
CoreStatistic Load (1.9ms) SELECT `client_jobs`.* FROM `client_jobs` 
    WHERE `client_jobs `.`client_id` IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10,........) 

第二個查詢遍歷數據庫中的每個client_job。這是從來沒有反對params[:job1], params[:job2]等測試,因此@clients回報nil撞毀我的視圖模板

(undefined method `map' for nil:NilClass 

回答

3

在我看來,一個更好的方法,然後自連接是簡單地加入ClientJobs然後用GROUP BYHAVING條款過濾出那些完全符合給定的相關記錄記錄。

performed_jobs = %w(job job2 job3) 
Client.joins(:client_jobs). 
     where(client_jobs: { job_name: performed_jobs }). 
     group("clients.id"). 
     having("count(*) = #{performed_jobs.count}") 

讓我們通過這個查詢:

  • 前兩個條款加入ClientJob s到Client S和只有那些過濾掉,有任何定義的三個工作(它使用IN條款)
  • 接下來,我們group這些加入記錄Client.id這樣我們得到客戶端
  • 最後,having子句確保我們只返回那些確實有3個ClientJob記錄加入的客戶端,即只有那些具有定義爲的全部三個客戶端作業的客戶端。

它與HAVING(COUNT(*) = ...)了轉動IN條款(其本質上是一種選項的OR -ed列表)變成了「必須具備所有這些」條款的伎倆。

+0

得到這個工作,但我有1個問題。我可以保留'perform_jobs'數組分配參數空字符串?問題是數組假定所有的選擇框都被使用過,所以數組的大小始終是3 – ctilley79

+0

當然,你總是可以做些像'perform_jobs = 。select {| j | j .present?}'。 – BoraMa

0

要在單個SQL查詢做這個嘗試以下操作:

jobs_with_same_user = ClientJob.select(:user_id).where(job_name: "<job_name1>", user_id: ClientJob.select(:user_id).where(job_name: "<job_name2>")) 

@clients = Client.where(id: jobs_with_same_user) 

下面是這個查詢是這樣做的:

  1. 選擇所有客戶端作業中的user_id s,其中[job_name2]
  2. 從(1)中選擇user_id IN的所有客戶端作業中的user_id s AND使[作業名稱1]
  3. 選擇所有使用(2)作爲子查詢的用戶。

沒有多少人知道這一點,但Rails 4+支持子查詢。基本上,這是自連接作爲子查詢的clients

SELECT * 
FROM clients 
WHERE id IN <jobs_with_same_user> 

而且,我不知道,如果你引用視圖中的client_jobs關聯,但如果你是,添加includes聲明,以避免在N + 1個查詢:

@clients = Client.includes(:client_jobs).where(id: jobs_with_same_user)

編輯

如果你願意的話,同樣的結果,可以實現用自引用內部聯接:

jobs_with_same_user = ClientJob 
     .select("client_jobs.user_id AS user_id") 
     .joins("JOIN client_jobs inner_client_jobs ON inner_client_jobs.user_id=client_jobs.user_id") 
     .where(client_jobs: { job_name: "<first_job_name1>" }, inner_client_jobs: { job_name: "<job_name2>" }) 

@clients = Client.where(id: jobs_with_same_user) 
+0

很好,但如果我想查詢第三份工作,該怎麼辦?我無法鏈接第三個子查詢。提出問題的查詢是答案中的第一個查詢。 'jobs_with_same_user = ClientJob.select(:user_id).where(job_name:「」.......' – ctilley79