ARel:向外部联接添加附加条件



我的Rails应用程序中有以下模型:

class Shift < ActiveRecord::Base
  has_many :schedules
  scope :active, where(:active => true)
end
class Schedule < ActiveRecord::Base
  belongs_to :shift
end

我希望生成所有活动轮班的集合,并热切地加载在两个给定日期之间具有occurs_on的任何相关时间表。如果一个班次在这两个日期之间没有时间表,那么它仍然应该在结果中返回。

本质上,我想生成相当于以下内容的SQL:

SELECT shifts.*, schedules.*
FROM shifts
  LEFT JOIN schedules ON schedules.shift_id = shifts.id
    AND schedules.occurs_on BETWEEN '01/01/2012' AND '01/31/2012'
WHERE shifts.active = 't';

我的第一次尝试是:

Shift.active.includes(:schedules).where("schedules.occurs_on BETWEEN '01/01/2012' AND '01/31/2012')

问题是occurs_on筛选是在where子句中完成的,而不是在联接中。如果某个班次在此期间没有时间表,则根本不会返回。

我的第二次尝试是使用joins方法,但这是一个内部联接。同样,这将取消所有在此期间没有时间表的班次。

我很沮丧,因为我知道我希望AREL生成的SQL,但我不知道如何用API表达它。任何人

您可以尝试一些非常原始的AREL。免责声明:我没有实际的ScheduleShift类,所以我无法正确测试它,但我在自己的机器上使用了一些现有的表来解决它。

on = Arel::Nodes::On.new(
  Arel::Nodes::Equality.new(Schedule.arel_table[:shift_id], Shift.arel_table[:id]).
  and(Arel::Nodes::Between.new(
    Schedule.arel_table[:occurs_on],
    Arel::Nodes::And.new(2.days.ago, Time.now)
  ))
)
join = Arel::Nodes::OuterJoin.new(Schedule.arel_table, on)
Shift.joins(join).where(active: true).to_sql

您可以使用SQL片段作为联接方法调用的参数:

Shift.active.joins('LEFT OUTER JOIN schedules ON schedules.occurs_on...')

您可以使用Arel构建一个原始sql查询,如下所示:

@start_date
@end_date
@shift = Shift.arel_table
@schedule = Schedule.arel_table
@shift.join(@schedule)
        .on(@schedule[:shift_id].eq(@shift[:id])
              .and(@schedule[:occurs_on].between(@start_date..@end_date)))
      .to_sql

相关内容

  • 没有找到相关文章

最新更新