【问题标题】:ActiveRecord Query for Detail and Summary Data详细和汇总数据的 ActiveRecord 查询
【发布时间】:2010-11-09 19:40:22
【问题描述】:

使用 Rails3,我有一个 downloads 表,其中包含 download_datecredits 列。我要生成的是这样的表:

 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 上运行。

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

【问题讨论】:

  • 我应该补充一点,优化很好(感谢 Fabio!)但我正在寻找的两件事是非常 Rails'ey 的方式来做到这一点,它不依赖于 Array#group_by,因为迭代内存中的一批 AR 对象只是为了分组,然后我必须重新访问它们进行求和。可能没有这样优雅的方式——我只是认为这是一个常见的用例。一直出现在会计领域。

标签: sql ruby-on-rails activerecord


【解决方案1】:

您所做的实际上已经非常优化。唯一的问题——你一次只下载一个。使用u.downloads.all.group_by(在其中添加all)以一次加载所有下载。这样一来,您几乎只需要一个查询。

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

【讨论】:

  • 我相信有一个对关联模型的反向引用,但我会使用急切加载来缓解这种情况。我的担忧是双重的: 1) 通常让数据库承担繁重的工作是一种更明智的卸载处理的方式——我想确保我没有遗漏任何东西;和 2) 在一定数量的行中,执行内存中的 group_by 会变得非常昂贵——我担心如果有大量流量,页面加载将开始变得棘手。我知道有些应用程序就是这样的。这似乎只是一个常见的用例,所以值得一问。
  • 问题是 - 无论谁创建信用额(红宝石或数据库),数据库都必须吐出所有下载。一旦你摆脱了这种担忧,在我看来,你应该晚上睡个好觉,直到真正需要注意的事情。到目前为止,您所做的一切都是正确的,额外的操作将是过早的优化。
【解决方案2】:

由于您已经在自己执行迭代,因此您也可以进行求和:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-11
    相关资源
    最近更新 更多