如何通过与多个范围的关联消除has_many中同一个表的多个连接?

时间:2014-12-05 22:58:25

标签: ruby-on-rails ruby-on-rails-3 ruby-on-rails-3.2

鉴于以下模型和协会:

class Employee
   has_many :positions
   has_many :titles, :through => :positions
   scope :is_active, -> { joins(:positions).merge(Position.is_active) }
   scope :title_is, ->(name) { joins(:titles).merge(Title.id_or_name_is(name)) }
end

class Position
   belongs_to :employee
   belongs_to :title
   # bool 'active' to indicate whether the position is active or not
   scope :is_active, -> { where("active = true") }
end

class Title
   has_many :positions
   has_many :employees, :through => :positions
   scope :id_or_name_is, ->(id_or_name) { where("titles.id = ? OR titles.name = ?", id_or_name, id_or_name) if id_or_name}
end

Employee.is_active返回正确的结果数并生成正确的查询。但是,当我尝试执行Employee.title_is(123).is_active时(我想要返回title_id为123并且处于活动状态的所有员工,它会导致多个连接与位置。这是没有问题的,除了它是活动的check仅适用于其中一个位置连接,从而导致活动位置和非活动位置:

SELECT COUNT(*)
FROM `employees`
    INNER JOIN `positions` ON `positions`.`employee_id` = `employees`.`id`
    INNER JOIN `positions` `positions_employees_join` ON positions_employees_join`.`employee_id` = `employees`.`id`
    INNER JOIN `titles` ON titles`.`id` = `positions_employees_join`.`title_id` 
WHERE positions.active = true)
      AND (titles.id = 123 OR 
      titles.name = 123)

我还应该注意,从技术上讲,如果我还有一种方法可以添加where子句positions_employees_join.active = true,那么它也可以正常工作,虽然我不知道如何在我的范围。

1 个答案:

答案 0 :(得分:0)

我会跳过使用titles范围内的title_is关联。

scope :title_is, ->(name) { joins(:positions => :title).merge(Title.is_or_name_is(name)) }

保持关联,因为在调用employee_instance.titles而不是employee_instance.positions.titles时,它仍然可以为您提供更有效的查询。

但是出于范围的目的,执行joins(:positions => :title)告诉ActiveRecord您要使用调用joins(:positions)的其他范围的常规连接。