1

我有一个名为 的表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 类上使它成为一个命名范围,以便它可以与其他条件结合,例如通过 a 过滤 Acts client_id?由于acts不在 my 中FROM,而是在 中LEFT JOIN,我猜不是,但也许有人知道方法。

编辑:澄清一下,无论数据库中有什么,此方法的目标是始终准确返回 7 个 Act 对象。

4

2 回答 2

2

如果你希望你的查询对象是可链接的,它必须是一个 ActiveRelation 对象

where, select,order和其他 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

于 2013-06-23T17:44:13.383 回答
1

虽然我真的认为我可以使用来自@house9 的解决方案,但我看不出有任何方法可以避免损害至少其中一个目标:

  1. 总是产生 7 个 Act 对象。
  2. 返回一个 ActiveRelation,以便我可以将此方法与其他范围组合。
  3. 允许加入clients表。

这是我实际使用的部分 SQL/部分 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_hours和的作用域在select_sum_total_fees其他地方使用,所以我想将它们拉到作用域中,而不是在几个大的原始 SQL 字符串中重复它们。

于 2013-06-24T21:00:23.403 回答