2010-11-09 67 views
3

使用Rails3,我有一个downloads表,download_datecredits列。我想生成一个表,如:ActiveRecord查询详细和汇总数据

Date  Credits 
2010-11-01 25 
2010-11-01 27 
*2010-11-01 52 <= Sum of previous 2 rows 
2010-11-02 32 
*2010-11-02 32 <= Sum of previous row 

这可以使用类似来完成:

u.downloads.group_by(&:download_date).each do |date, downloads| 
    downloads.each do |d| 
    puts " %10s %3d" % [d.download_date, d.credits] 
    end 
    puts "*%10s %3d" % [date, downloads.sum(&:credits)] 
end 

此解决方案,同时它的工作原理,是不是太Rails的样,并导致相当发出大量的SQL查询。假设每100个用户x 10,000次下载/年,并且每年年底查询计数为每个这些页面的1,000,000次。

我想出的任何解决方案都应该是数据库不可知的,如果可能的话。我知道我将在Heroku上使用PostgreSQL进行部署,但是我的开发版本(无论如何被误导)仍然在MySQL上运行。

我希望我已经提供了有关问题域和涉及问题的足够信息。任何意见或建议?

+0

我应该补充说优化是好的(谢谢法比奥!)但我特别寻找的两件事是Rails'ey的方式来做到这一点,不依赖Array#group_by,因为迭代内存一批AR对象只是为了分组,然后我必须重新访问它们以进行求和。可能没有这种优雅的方式 - 我只是认为这是一个常见的用例。始终在会计上出现。 – 2010-11-09 20:33:27

回答

1

你在做什么其实是蛮已经过优化。唯一的问题 - 你一次只能下载一个下载。使用u.downloads.all.group_by(在那里添加all)以便一次加载所有下载。这样你就可以简化为一个查询。

更新:尽管第二次看,在Rails 3中,这甚至不应该产生多个查询。有很多原因可能导致您遇到N + 1问题。例如,如果您从视图某处的某个关联对象反向引用某个特定下载,则rails可能不知道该下载已从数据库中作为数组的一部分获取,然后重新获取它。如果您看到很多查询 - 此代码不应该导致它们。

+0

我相信有一个对相关模型的反向引用,但我会使用热切加载来检查出来以减轻这种影响。我的担忧是双重的:1)通常让数据库执行繁重的工作是卸载处理的一种更智能的方法 - 我想确保我不会错过任何东西; 2)在一定数量的行中,执行内存中的group_by变得非常昂贵 - 我担心如果有任何大量的流量,页面加载将开始得到pokey。我认识到,有这样的应用程序*就是*。它只是一个常见的用例,所以值得一提。 – 2010-11-10 16:54:12

+0

事情是 - 无论谁创造信贷总额(红宝石或数据库),数据库必须吐出所有的下载。一旦你过去了这种担忧,在我看来,你应该晚上睡得很好,直到真正需要关注的东西。到目前为止,你做的一切都是正确的,额外的行动将会是过早的优化。 – 2010-11-10 23:10:07

0

既然你已经自己进行迭代,你也可以做的总和:

u.downloads.group_by(&:download_date).each do |date, downloads| 
    subtotal_credits = 0 
    downloads.each do |d| 
    puts " %10s %3d" % [d.download_date, subtotal_credits += d.credits] 
    end 
    puts "*%10s %3d" % [date, subtotal_credits] 
end