如何使用Arel将表列作为子句条件引用?

时间:2014-12-12 14:21:15

标签: ruby-on-rails activerecord arel

我有这个范围:

scope :in_range, lambda { |begin_time, end_time, excluded_sales = nil|
  where(
    'start_at BETWEEN ? AND ? OR finish_at BETWEEN ? AND ? OR ? BETWEEN start_at AND finish_at OR ? BETWEEN start_at AND finish_at',
  begin_time, end_time, begin_time, end_time, begin_time, end_time)
  .where.not(id: excluded_sales)
}

这会产生

> Sale.in_range(Time.now, Time.now + 1)
  Sale Load (0.6ms)  SELECT "sales".* FROM "sales"  WHERE (start_at BETWEEN '2014-12-12 10:45:47.065712' AND '2014-12-12 10:45:48.065714' OR finish_at BETWEEN '2014-12-12 10:45:47.065712' AND '2014-12-12 10:45:48.065714' OR '2014-12-12 10:45:47.065712' BETWEEN start_at AND finish_at OR '2014-12-12 10:45:48.065714' BETWEEN start_at AND finish_at) AND ("sales"."id" IS NOT NULL)

我需要使用Arel重写这个范围,因为我相信我不应该在这里使用原始SQL语法。 但我不知道如何使用in方法与表列引用。我尝试了这个但却异常:

> s = Sale.arel_table
> begin_time = Time.now
> Sale.where(begin_time.in(s[:start_at]..s[:finish_at]))
  --> ArgumentError: bad value for range

也尝试了这个,但没有运气:

> s = Sale.arel_table
> begin_time = Time.now
> Sale.where(begin_time.in('sales.start_time'..'sales.end_time')
  --> NoMethodError: undefined method `round' for "sales.end_time".."sales.start_time":Range

请告知如何使用Arel引用表列作为子句条件?

谢谢!

更新:

OR语句有四个条件:

  1. start_at,范围为begin_timeend_time
  2. finish_at,范围为begin_timeend_time
  3. begin_time,范围为start_timefinish_time
  4. end_time,范围为start_timefinish_time
  5. 基本上它只是一个日期范围重叠。

    我把它改写成

    scope :in_range, lambda { |date_range, excluded_sales = nil|
      s = Sale.arel_table
      where(
        s.grouping(s[:starts_at].gteq(date_range.first).and(s[:starts_at].lteq(date_range.last)))
          .or(s.grouping(s[:ends_at].gteq(date_range.first).and(s[:ends_at].lteq(date_range.last))))
          .or(s.grouping(s[:starts_at].lteq(date_range.first).and(s[:ends_at].gteq(date_range.last))))
          .or(s.grouping(s[:starts_at].gteq(date_range.first).and(s[:ends_at].lteq(date_range.last))))
      ).where.not(id: excluded_sales)
    }
    

    也许有一种更简单的方法来实现我的目标?

1 个答案:

答案 0 :(得分:0)

据我所知,您希望选择所有记录,哪个范围与指定范围有交集?如果是这样,只需选择记录,其中start_at字段值在范围结束之前,并且具有在范围开始之后的finish_at字段值。因此应该是:

 scope :in_range, proc do |begin_time, end_time, excluded_sales|
    where(arel_table[:start_at].lteq(end_time)
     .and(arel_table[:finish_at].gteq(begin_time))
     .and(arel_table[:id].not_eq(excluded_sales)))
 end