Rails按以下方式对表进行分组:created_at,返回:status列的计数,然后将:status列分组,每个唯一值的计数

我有一个模型通知,我想知道每天创建多少通知以及每个状态有多少通知。 status是模型Notification的字符串键。

 Notification.where(user: users).group('DATE(created_at)').count('created_at') 

那给了我:

 {Mon, 05 Oct 2015=>2572, Tue, 06 Oct 2015=>555, Wed, 07 Oct 2015=>2124, Thu, 08 Oct 2015=>220, Fri, 09 Oct 2015=>36} 

但我想要像:

 {Mon, 05 Oct 2015=>{total=>2572, error=>500, pending=>12}, Tue, 06 Oct 2015=>{total=>555, sent=>50, pending=>12}} 

或类似的。 活动记录是否可能? 也许group_by对我有好处。 但它被弃用了。

目前我尝试过:

 Notification.where(user: users).group('DATE(created_at), status').count('created_at') # => {"error"=>2, "sent"=>42} 

这不是我想要的结果。

我正在添加第二个答案,采用不同的方法,我认为它更好,因为它是高效的,可以转换为DB视图。

任何时候我最终都会在数据库上进行大量重复点击或者无法很好地转换的大型复杂查询,我会使用纯SQL,因为它可以用作数据库中的视图。 我问这个问题是因为我的SQL很差。 我认为这可以适应您的需求,特别是如果“状态”字段是一组已知的可能值。 以下是我最初尝试的方法:

构造一个有效的SQL查询。 你可以在psql中测试它。

 SELECT created_at, count(status) AS total, sum(case when status = 'error' then 1 end) AS errors, sum(case when status = 'pending' then 1 end) AS pending, sum(case when status = 'sent' then 1 end) AS sent FROM notifications GROUP BY created_at; 

这应该返回一个数据透视表,如:

 | created_at |total|errors|pending|sent| ---------------------------------------------- | Mon, 05 Oct 2015 |2572 |500 |12 |null| | Tue, 06 Oct 2015 |555 |null |12 |50 | 

好的,任何单个表都是Rails中的一个简单查询,它将把它作为一个对象数组加载。 每个对象都有一个与每列对应的方法。 如果该行为该行为null,则Rails将传递nil作为值。

在Rails中测试它

 @stats = Notification.where(user: users).find_by_sql("SELECT created_at, count(status) AS total, sum(case when status = 'error' then 1 end) AS errors, sum(case when status = 'pending' then 1 end) AS pending, sum(case when status = 'sent' then 1 end) AS sent FROM notifications GROUP BY created_at;") 

这将返回一组Notification对象……

 => [#< Notification id: nil, created_at: "2014-02-07 22:36:30"> #< Notification id: nil, created_at: "2014-06-26 02:07:51">, #< Notification id: nil, created_at: "2015-04-26 21:37:09">, #< Notification id: nil, created_at: "2014-02-07 22:48:29">, #< Notification id: nil, created_at: "2014-11-04 23:39:07">, #< Notification id: nil, created_at: "2015-01-27 17:46:50">,...] 

请注意, Notification id:为零。 这是因为这些对象不代表数据库中的实际对象,而是表示查询生成的表中的一行。 但现在你可以这样做:

 @stats.each do |daily_stats| puts daily_stats.attributes end #{"created_at" => "Mon, 05 Oct 2015", "total" = 2572, "errors" => 500, "pending" => 12, "sent" => nil} #{"created_at" => "Tue, 06 Oct 2015", "total" = 555, "errors" => nil, "pending" => 12, "sent" => 50} 

你的@stats变量很容易传递给一个视图,它可以很容易地打印成.html.erb文件中的.html.erb 。 您可以访问数组中任何Notification对象的属性,如:

 @stats[0].created_at #=> "Mon, 05 Oct 2015" @stats[1].pending #=> 12 

总的来说,您使用了一个查询来获取整个数据集。

将其存储为视图登录数据库中的SQL控制台即可

 CREATE VIEW daily_stats AS SELECT user_id, created_at, count(status) AS total, sum(case when status = 'error' then 1 end) AS errors, sum(case when status = 'pending' then 1 end) AS pending, sum(case when status = 'sent' then 1 end) AS sent FROM notifications GROUP BY user_id, created_at; 

现在你可以得到结果了

 Select * FROM daily_stats; 

请注意,由于您在原始问题中并且将user_id添加到SELECT,因此我故意不会将此限制为用户。 我们正在直接在DB中工作,它应该可以轻松处理从该视图生成一个表,其中包含每个日期的所有用户统计信息。 这是一个非常强大的数据集,可用于您正在做的事情。 现在,您可以在Rails中设置虚拟模型,并轻松地使所有数据都可用,而无需扭曲Rails查询。

添加虚拟模型 app / models / daily_stat.rb:

 class DailyStat < ActiveRecord::Base belongs_to :user #this is a model for a view in the DB called dash_views #class name is singular and will automatically look for the table "daily_stats" which his snake_case and plural. end 

将相应的关系添加到您的User模型:

 class User < ActiveRecord::Base has_many :daily_stats end 

现在,您可以通过非常棒的方式访问您的统计数据。

 users = [2] DailyStat.where(user: users) => AllStat Load (2.8ms) SELECT "all_stats".* FROM "all_stats" WHERE "all_stats"."category_id" = 2 => [ #, #, #...] 

而在另一个方向:

 user = User.first user.daily_stats #returns array of that users DailyStat objects. 

关键是“解决最低层次的问题”。 解决数据库中的数据查询问题,然后使用Rails来操作和呈现它。

这对我来说很难测试可能的答案,因为日期有点独特,查询稍微复杂一些。 但这里是如何在哈希中得到你的计数:

 my_hash = {} #create your empty hash notifications = Notification.where(user: users).select(:created_at).uniq #get a hash of Notification objects with unique created_at dates notifications.each do |n| my_hash[n.created_at] = Notification.where(created_at: n.created_at).select(:status).group(:status).count #load your hash with  => {"error" => 20, "pending" => 30} my_hash[n.created_at]["total"] = Notification.where(created_at: n.created_at).count #add the total count to your hash end 

编辑

随着OP的发现,随着查询日期的数量增加,这变得非常低效。 那是因为它正在为第一个查询中返回的每个日期调用一个查询。 我确信有一种方法可以将其作为一个长SQL查询来执行,并将其用作数据库中的视图。