【问题标题】:Rails: ActiveRecord scope based on property of childRails:基于子属性的 ActiveRecord 范围
【发布时间】:2013-08-30 02:22:29
【问题描述】:

我有三个模型Component、Version 和User,它们相互关联,如下所示:

class Component < ActiveRecord::Base
    has_many :versions, class_name 'ComponentVersion'
end

class ComponentVersion < ActiveRecord::Base
    belongs_to :component
    belongs_to :approver, class_name: 'User'
end

class User < ActiveRecord::Base
end

我想在Component 上创建两个作用域:approved 和 unapproved。当一个组件至少有一个被批准的version 时,它被“批准”,而一个版本在其approver_id 不是nil 时被批准。所以Component.approved 应该返回所有批准的组件,Component.unapproved 应该返回所有未批准的版本。

不幸的是,我不知道该怎么做。

我可以很容易地在 ComponentVersion 上定义范围:

class ComponentVersion < ActiveRecord::Base
    belongs_to :component
    belongs_to :approver, class_name: 'User'
    scope :approved,   -> { where('approver_id IS NOT NULL') }
    scope :unapproved, -> { where('approver_id IS NULL') }
end 

然后在控制台Component.joins(:version).merge(ComponentVersion.approved) 给了我一些接近我想要的Component.approved 的东西,除了它包含重复项(如果一个组件有多个批准的版本,它会被多次返回。)

而Component.joins(:version).merge(ComponentVersion.unapproved) 则相反,因为它返回的组件同时具有未经批准和批准的版本,而我只想要 仅 具有未经批准的版本(或根本没有版本)的组件。

我还尝试将包含 OUTER JOIN 而不是 INNER 的 SQL 查询传递给 joins,但它们并没有给我想要的东西,而且它们扩展了我对 SQL 的有限理解。

如何获得我需要的查询?

(我使用的是 Postgres 和 Rails 3.2.13,以防万一。)

【问题讨论】:

    标签: ruby-on-rails activerecord scope named-scope


    【解决方案1】:

    使用查询生成器 DSL 似乎不可能得到你想要的东西。我通常为此使用相关子查询。

    class Component
      scope :approved, lambda{ where('EXISTS (SELECT 1 FROM component_versions WHERE component_versions.component_id = components.id AND component_versions.approver_id IS NOT NULL)') }
    end
    

    另一个版本

    class Component
      scope :approved, lambda { where('id IN (SELECT c.id FROM components c JOIN component_versions cv ON c.id = cv.component_id AND cv.approver_id IS NOT NULL)' }
    end
    

    使用 Postgres 可能并不重要,您将使用哪个,它有一个非常好的查询计划器。

    【讨论】:

    • 很有魅力!尽管第二个示例缺少结束 )。但是如何定义unapproved 范围?
    • 使用NOT EXISTS 或NOT IN。似乎 PostgreSQL 优化器没有我想象的那么好,所以坚持使用第二个版本。作为奖励,它更容易理解。顺便说一句,你真的应该考虑学习一点 SQL,通过子查询,你将准备好处理 Web 开发中出现的 98% 的数据库访问任务。
    猜你喜欢
    • 2015-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-16
    相关资源
    最近更新 更多