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')

問題は、ボランティアが今まで何の関与もしていない可能性があり、その場合、クエリはそれらのボランティアを完全に無視することです。エンゲージメントがない場合でも、すべてのボランティアを返すにはどうすればよいですか?

4

2 に答える 2

2

@アンソニーはすでに言及しNOT EXISTSました。もう 1 つのオプションは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'を結合句に移動する必要があります。

不足している可能性がある場合は、別LEFT JOINのものを使用してください。しかし、とaccountsをチェックしたいようです。user_typeactivation_state

于 2012-07-30T15:38:38.537 に答える
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]) }
于 2012-07-30T15:09:13.787 に答える