Rails命名左连接模型的范围

时间:2013-06-23 03:43:06

标签: ruby-on-rails postgresql activerecord

我有一个名为acts的表,我想运行一个查询,将整个行动值累计一整周。我想确保查询总是为一周中的每一天返回一行,即使当天没有记录。现在我这样做:

  def self.this_week_totals
    sunday = Time.now.beginning_of_week(:sunday).strftime("%Y-%m-%d")
    connection.select_values(<<-EOQ)
        SELECT  COALESCE(SUM(end_time - start_time), '0:00:00') AS total_time
        FROM    generate_series(0, 6) AS s(t)
        LEFT JOIN acts
        ON      acts.day = '#{sunday}'::date + s.t
        WHERE   deleted_at IS NULL
        GROUP BY s.t
        ORDER BY s.t
    EOQ
  end

我有什么方法可以将它作为Act类的命名范围,以便它可以与其他条件结合使用,例如以client_id过滤使徒行传?由于acts不在我的FROM中,而是LEFT JOIN的一部分,我猜不会,但也许有人知道某种方式。

编辑:只是为了澄清,目标是这个方法总是返回7个Act对象,无论数据库中有什么。

2 个答案:

答案 0 :(得分:2)

如果您希望查询对象是可链接的,则它必须是ActiveRelation对象

whereselectorder和其他Arel对象返回可链接的ActiveRelation对象,因此如果以下工作可以链接返回的查询对象

注意在rails 3及以上版本中,返回ActiveRelation的类方法与作用域基本相同,它们都是可链接的查询对象

class Act
  def self.this_week_totals
    sunday = Time.now.beginning_of_week(:sunday).strftime("%Y-%m-%d")
    select("COALESCE(SUM(end_time - start_time), '0:00:00') AS total_time")
      .from("generate_series(0, 6) AS s(t)")
      .joins("LEFT JOIN acts ON acts.day = '#{sunday}'::date + s.t")
      .where("deleted_at IS NULL")
      .group("s.t")
      .order("s.t")
  end
  # ...
end

client_id = params[:client_id]
Act.this_week_totals.where("client_id = ?", client_id)

http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-from

答案 1 :(得分:1)

虽然我真的认为我可以使用@ house9的解决方案,但我认为没有办法避免至少在其中一个目标上妥协:

  1. 总是产生7个Act对象。
  2. 返回一个ActiveRelation,以便我可以将此方法与其他范围组合。
  3. 允许加入clients表。
  4. 以下是我实际使用的part-SQL / part-Ruby解决方案,遗憾地放弃了上面的#2点,并且还返回了元组而不是Acts:

    def self.this_week(wk=0)
      sunday = Time.now.beginning_of_week(:sunday)
      sunday += wk.weeks
      not_deleted.where("day BETWEEN ? AND ?", sunday, sunday + 7.days)
    end
    
    scope :select_sum_total_hours,
      select("EXTRACT(EPOCH FROM COALESCE(SUM(end_time - start_time), '0:00:00'))/3600 AS total_hours")
    
    scope :select_sum_total_fees,
      joins(:client).
      select("SUM(COALESCE(clients.rate, 0) * EXTRACT(EPOCH FROM COALESCE(end_time - start_time, '0:00:00'))/3600) AS total_fees")
    
    
    def self.this_week_totals_by_day(wk=0)
      totals = Hash[
        this_week(wk)
          .select("EXTRACT(DAY FROM day) AS just_day")
          .select_sum_total_hours
          .select_sum_total_fees
          .group("day")
          .order("day")
          .map{|act| [act.just_day, [act.total_hours.to_f, act.total_fees.to_money]]}
      ]
      sunday = Time.now.beginning_of_week(:sunday)
      sunday += wk.weeks
      (0..6).map do |x|
        totals[(sunday + x.days).strftime("%d")] || [0, 0.to_money]
      end
    end
    

    这可能会有点干扰,如果有一个月只有不到7天,它会产生错误,但希望它能显示我正在做的事情。 this_weekselect_sum_total_hoursselect_sum_total_fees的范围在其他地方使用,因此我想将它们移到范围中,而不是在几个大的原始SQL字符串中重复它们。