2012-07-30 110 views
0

在我正在工作的Rails應用程序,我有以下設置:選擇記錄根據具體的情況從另一個表

class Volunteer < AR::Base 
    has_many :engagements 
end 

class Engagement < AR::Base 
    belongs_to :volunteer 
end 

我想選擇誰不是在某一特定日期從事所有的志願者或日期範圍之間:

scope :not_engaged_on, lambda { |q| joins(:engagements).where(["engagements.date != ?", q])} 

scope :not_engaged_between, lambda { |s, e| joins(:engagements).where(["engagements.date NOT BETWEEN ? AND ?", s, e]) } 

這些範圍我正與其他範圍合併來過濾志願者。

以上範圍:not_engaged_between產生以下SQL:

SELECT "volunteers".* FROM "volunteers" INNER JOIN "accounts" ON "accounts"."user_id" = "volunteers"."id" AND "accounts"."user_type" = 'Volunteer' INNER JOIN "engagements" ON "engagements"."volunteer_id" = "volunteers"."id" WHERE (accounts.activation_state = 'active') AND (engagements.date NOT BETWEEN '2012-07-30' AND '2012-08-04') 

的問題是志願者可能沒有任何訂婚到現在,並在這種情況下,查詢完全不理會那些志願者。即使他們沒有任何交往,我如何能夠讓所有志願者回歸?

回答

1

你需要一個not exists來做到這一點。這個語法在SQL上有點沉重,也許有更好的辦法來做到這一點。

scope :not_engaged_on, lambda { |q| where(["NOT EXISTS (SELECT ID from engagements WHERE engagements.date = ? AND volunteer_id = volunteers.id)", q])} 

scope :not_engaged_between, lambda { |s, e| where(["NOT EXISTS (SELECT ID from engagements WHERE engagements.date BETWEEN ? AND ? AND volunteer_id = volunteers.id)", s, e]) } 
+0

感謝您的回答。但是,拋出NoMethodError:未定義的方法'default_scoped?'爲#。有任何想法嗎?? – 2012-07-30 15:18:37

+0

確定編輯我的答案。可能不是最好的語法,但它應該做的工作 – 2012-07-30 15:31:05

+0

感謝您的幫助。不知何故,exists.not語法和NOT EXISTS不適合我。必須這樣做才能使其工作:scope:not_engaged_between,lambda {| s,e |加入(「LEFT JOIN engagements e ON e.volunteer_id = volunteers.id AND e.date NOT BETWEEN'#{s}'AND'#{e}'」)。where([「e.volunteer_id IS NULL」])} ,它將按照@Erwin的建議生成SQL。再次感謝你的幫助! – 2012-07-30 16:18:55

2

@Anthony已經提到NOT EXISTS。另一種選擇是一個LEFT [OUTER] JOIN +檢查它是否存在(e.volunteer_id IS NULL):


SELECT v.* 
FROM volunteers  v 
JOIN accounts   a ON a.user_id = v.id 
LEFT JOIN engagements e ON e.volunteer_id = v.id AND e.date BETWEEN '2012-07-30' AND '2012-08-04' 
WHERE a.user_type = 'Volunteer' 
AND a.activation_state = 'active' 
AND e.volunteer_id IS NULL; 

你必須條件e.date BETWEEN '2012-07-30' AND '2012-08-04'移動到在這種情況下連接語句。

對於accounts使用另一個LEFT JOIN,如果它也可能丟失。但看來你確實想要檢查user_typeactivation_state

+1

謝謝。它的工作原理是,我必須像這樣定義它來生成所需的SQL:scope:not_engaged_between,lambda {| s,e |加入(「LEFT JOIN engagements e ON e.volunteer_id = volunteers.id AND e.date NOT BETWEEN'#{s}'AND'#{e}'」)。where([「e.volunteer_id IS NULL」])} 。再次感謝!! – 2012-07-30 16:17:45

相關問題