【问题标题】:Selecting records in on table based on conditions from another table根据另一个表中的条件选择表中的记录
【发布时间】:2012-07-30 14:58:38
【问题描述】:

在我正在开发的 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')

问题是志愿者可能直到现在还没有任何参与,在这种情况下,查询完全忽略了这些志愿者。即使他们没有任何参与,我如何才能让所有志愿者返回?

【问题讨论】:

    标签: sql ruby-on-rails-3 postgresql join scope


    【解决方案1】:

    @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

    【讨论】:

    • 谢谢。它有效,我必须像这样定义它以生成所需的 SQL:scope :not_engaged_between, lambda { |s, e| joins("LEFT JOIN 参与 e ON e.volunteer_id = 志愿者.id AND e.date NOT BETWEEN '#{s}' AND '#{e}'").where(["e.volunteer_id IS NULL"]) } .再次感谢!!
    【解决方案2】:

    您需要not exists 来执行此操作。这种语法对 SQL 来说有点重,也许有更好的 arel-way 来做到这一点。

    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]) }
    

    【讨论】:

    • 感谢您的回答。但是,抛出 NoMethodError: undefined method `default_scoped?'对于#<:nodes::not:0xcfc1b04>。有什么想法吗??
    • 好的,编辑了我的答案。可能不是最好的语法,但它应该可以完成这项工作
    • 感谢您的帮助。不知何故,exists.not 语法和 NOT EXISTS 对我不起作用。必须这样做才能使其工作:scope :not_engaged_between, lambda { |s, e| joins("LEFT JOIN 参与 e ON e.volunteer_id = 志愿者.id AND e.date NOT BETWEEN '#{s}' AND '#{e}'").where(["e.volunteer_id IS NULL"]) } ,这将按照@Erwin 的建议生成 SQL。再次感谢您的帮助!
    • 因为我在运行查询之前构建范围并合并它们,所以这个方法现在对我来说效果更好,谢谢。
    猜你喜欢
    • 1970-01-01
    • 2010-12-14
    • 1970-01-01
    • 2023-04-07
    • 2020-07-28
    • 1970-01-01
    • 2021-01-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多