简体   繁体   English

如何按日期计数获得此postgres组包括0天

[英]How to get this postgres group by date count include days with 0

I have this Postgres query to select all users from the last 30 days and group by count. 我有这个Postgres查询来选择过去30天内的所有用户并按计数分组。

@users = User.where('created_at >= ?', last_30_day).count(:order => 'DATE(created_at) ASC', :group => ["DATE(created_at)"])

The problem is that is returning like this 问题是这样返回

2014-01-15     6
2014-01-13     2
2014-01-09     1

And I would like to get the days with cero results as well like this 而且我希望得到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

Any idea how to do this? 知道怎么做吗?

To expound on @mu_is_too_short answer you can do it using generate_series like so: 要解释@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

Which yields like: 产量如下:

  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

Futhermore the ActiveRecord syntax for this is quite ugly, your best bet is to write it in the pure SQL above and just retrieve your raw results. 此外,ActiveRecord语法非常难看,最好的办法是在上面的纯SQL中编写它,然后检索原始结果。 Something like: 就像是:

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

Because <<-SQL is interpolated you can use variables inside to adjust your query. 因为<<-SQL是插值的,所以你可以使用里面的变量来调整你的查询。

With a short list like you have, the easiest thing would be to generate a hash with the desired dates: 使用像您一样的简短列表,最简单的方法是生成具有所需日期的哈希:

zeros = 30.downto(0)
          .map { |n| n.days.ago.to_date.iso8601 }
          .each_with_object({}) { |d, h| h[d] = 0 }

and then merge the database's results into it: 然后将数据库的结果合并到其中:

@users = zeros.merge(User.where(...).count(...))

If you wanted to do it all inside the database then you could OUTER JOIN with a derived table generated from generate_series : 如果你想在数据库中完成所有操作,那么你可以使用generate_series 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

Trying to get AR to produce that SQL would be a bit messy and probably not worth it for only a month's worth of data. 试图让AR产生SQL会有点混乱,而且可能只值一个月的数据就不值得。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM