1

我有三个模型Component,它们相互关联,如下所示VersionUser

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:approvedunapproved. 当一个组件至少有一个被批准时,它被“批准” 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)组件时,它会返回同时具有未批准版本和已批准版本的组件。

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

我怎样才能得到我需要的查询?

(我正在使用 Postgres 和 Rails 3.2.13,以防万一。)

4

1 回答 1

3

使用查询生成器 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,您使用哪一个可能并不重要,它有一个非常好的查询计划器。

于 2013-08-27T11:24:01.827 回答