我有这个Postgres查询来选择过去30天内的所有用户并按计数分组。
@users = User.where('created_at >= ?', last_30_day).count(:order => 'DATE(created_at) ASC', :group => ["DATE(created_at)"])
问题是这样返回
2014-01-15 6
2014-01-13 2
2014-01-09 1
我希望得到cero结果的日子也是如此
2014-01-15 6
2014-01-14 0
2014-01-13 2
2014-01-12 0
2014-01-11 0
2014-01-10 0
2014-01-09 1
知道怎么做吗?
答案 0 :(得分:5)
要解释@mu_is_too_short答案,你可以使用generate_series
这样做:
SELECT dt.series, COUNT(u.created_at)
FROM users u
RIGHT OUTER JOIN (
SELECT
GENERATE_SERIES( (NOW() - INTERVAL '30 day')::date, NOW()::date, '1 day')::date
AS series
) AS dt
on DATE_TRUNC('month', u.created_at) = dt.series
GROUP BY dt.series
ORDER BY dt.series DESC
产生的结果如下:
series | count
------------+-------
2014-01-08 | 0
2014-01-07 | 0
2014-01-06 | 0
2014-01-05 | 0
2014-01-04 | 0
2014-01-03 | 0
2014-01-02 | 0
2014-01-01 | 1
2013-12-31 | 0
此外,ActiveRecord的语法非常难看,最好的办法是在上面的纯SQL中编写它,然后只检索原始结果。类似的东西:
sql = <<-SQL
SELECT dt.series, COUNT(u.created_at)
FROM users u
RIGHT OUTER JOIN (
SELECT
GENERATE_SERIES( (NOW() - INTERVAL '30 day')::date, NOW()::date, '1 day')::date
AS series
) AS dt
on DATE_TRUNC('month', u.created_at) = dt.series
GROUP BY dt.series
ORDER BY dt.series DESC
SQL
records = User.connection.select_all(sql)
records.each do |record|
puts record['series']
end
因为插入<<-SQL
,您可以使用内部变量来调整查询。
答案 1 :(得分:3)
使用您所拥有的简短列表,最简单的方法是生成具有所需日期的哈希:
zeros = 30.downto(0)
.map { |n| n.days.ago.to_date.iso8601 }
.each_with_object({}) { |d, h| h[d] = 0 }
然后将数据库的结果合并到其中:
@users = zeros.merge(User.where(...).count(...))
如果你想在数据库中完成所有操作,那么你可以使用从generate_series
生成的派生表进行OUTER JOIN:
select dt.d, count(*)
from (
select current_date - 30 + i
from generate_series(0, 30) as gs(i)
) as dt(d)
left join user u on date(u.created_at) = dt.d
试图让AR产生SQL会有点混乱,而且可能只值一个月的数据就不值得。