2012-07-30 124 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