SubQuery在ActiveRecord中聚合

时间:2015-02-03 17:04:51

标签: sql ruby-on-rails ruby-on-rails-3 activerecord

我试图避免在我的Rails应用程序中使用直接SQL,但需要做一个相当大的版本:

SELECT ds.product_id,
  ( SELECT SUM(units) FROM daily_sales WHERE (date BETWEEN '2015-01-01' AND '2015-01-08') AND service_type = 1 ) as wk1,
  ( SELECT SUM(units) FROM daily_sales WHERE (date BETWEEN '2015-01-09' AND '2015-01-16') AND service_type = 1 ) as wk2
FROM daily_sales as ds group by ds.product_id

我确信它可以做到,但我很难把它写成一个积极的记录声明。有人可以帮忙吗?

3 个答案:

答案 0 :(得分:1)

如果必须在单个查询中执行此操作,则需要为CASE语句编写一些SQL。您需要以下内容:

ranges = [ # ordered array of all your date-ranges
  Date.new(2015, 1, 1)..Date.new(2015, 1, 8),
  Date.new(2015, 1, 9)..Date.new(2015, 1, 16)
]

overall_range = (ranges.first.min)..(ranges.last.max)

grouping_sub_str = \
  ranges.map.with_index do |range, i|
    "WHEN (date BETWEEN '#{range.min}' AND '#{range.max}') THEN 'week#{i}'"
  end.join(' ')

grouping_condition = "CASE #{grouping_sub_str} END"
grouping_columns = ['product_id', grouping_condition]

DailySale.where(date: overall_range).group(grouping_columns).sum(:units)

这将生成带有数组键和数值的哈希。密钥的格式为[product_id, 'week1'],其值将为该周的units的相应总和。

答案 1 :(得分:0)

将您的SQL简化为以下内容并尝试转换它..

SELECT ds.product_id,
, SUM(CASE WHEN date BETWEEN '2015-01-01' AND '2015-01-08' AND service_type = 1
    THEN units
    END) WK1
    , SUM(CASE WHEN date BETWEEN '2015-01-09' AND '2015-01-16' AND service_type = 1
    THEN units
    END) WK2 
FROM daily_sales as ds 
group by ds.product_id

答案 2 :(得分:0)

每个铁路开发人员迟早都会撞到Active Record query interface的墙壁,只是为了找到Arel中的解决方案。

Arel为您提供了创建查询所需的灵活性,而无需使用循环等。我不会给出可运行的代码,而是提示如何自己完成:

  1. 我们将使用arel_table来创建查询。对于名为Product的模型,获取Arel表就像products = Product.arel_table
  2. 一样简单
  3. 获取列的总和就像daily_sales.project(daily_sales[:units].count).where(daily_sales[:date].gt(BEGIN_DATE).where(daily_sales[:date].lt(END_DATE)。您可以根据需要链接尽可能多的where,并将其转换为SQL AND
  4. 由于我们需要在最终结果中包含多个总和,因此您需要使用Common Table Expressions(CTE)。有关详细信息,请查看文档和this answer
  5. 您可以将步骤3中的CTE与group结合使用,您就完成了!