使用ActiveRecord高效生成报告

我们需要生成报告,这些报告可以提取大量数据,运行一些计算并将它们作为较大表的一部分进行吐出。 这样做并不困难。 但是,使它可以使用现有方法并且不生成1000个SQL查询要困难得多。

例如,我可能有一个Account类,其方法如下:

 def balance_at(time=Time.now) payments_out = self.payments.where("created_at <= ?",time).sum("amount") payments_in = self.payments_on_account.where("created_at <= ?",time).sum("amount") payments_in - payments_out end 

这可用于在月初和最后获得帐户余额。 它很棒。

但是,如果我想要一个包含月初和月末所有Account余额的表格,那么事情就会变得愚蠢。 例如:

 Account.includes(:payments, :payments_on_account) 

如果我想完全用Ruby来解决这个问题,那么将获得我需要的所有数据,但是我很好的小方法balance_at并没有完成Ruby中的所有数字运算(对于个别情况来说会很慢)。

我可以用Ruby和SQL中的东西解决它,具体取决于缓存的内容,如下所示:

 def balance_at(time=Time.now) payments_out, payments_in = [payments, payments_on_account].map{|payments| if payments.loaded? payments.find_all{|p| p.created_at < time }.inject(0){|a,p| p.amount + a } else payments.where("created_at <= ?",time).sum("amount") end } payments_in - payments_out end 

但是,这也不是可读或易于测试。

你会如何解决它?

我参与了一些需要报告的项目。 Web应用程序堆栈不是进行报告的最佳位置,但似乎开源报告选项相当有限。 但并非每个组织都可以使用SSRS或Crystal,根据我的经验,这些产品很痛苦,并且引入的问题多于必要的。

我正在使用视图来完成这些工作。 SQL专为分组和聚合数据而设计,它比ruby更能处理这些内容。 但是,大多数情况下,视图将在运行中执行,所以这并不像你在这里获得了性能提升。 理想情况下,在获得基本实现后,您可以设置一些cron任务或预先计算数据的方法。 如果要经常访问您的报告,并且在白天,您将需要一个专用的报告数据库。 如果报告必须包含实时数据,则需要设置复制。

我知道,在Ruby / Rails中搞乱SQL会很乱并且不满意。 所以我编写了一个名为Skiima的gem,它可以帮助您管理项目中可能存在的无关SQL对象。 通过加载迁移,可以更轻松地测试这些迁移。

http://github.com/dcunited001/skiima

除此之外,这就是我一直在做的事情:

 class AccountsReport < ActiveModel attr_accessor :items def initialize(attr = {}) # read in params, set attrs end def execute get_report_items group_report_items summarize_report_groups # if this needs to occur outside of sql end end class AccoutsReportItem < ActiveRecord::Base # you can hook into a view here, you will want the view to return an id col set_table_name :view_accounts_report_items end # yay for arel and activerecord methods. # you can even set up relationships on these. use sparingly. # AccountsReportItem.where(:blah => 'balah') 

假设你有1000个帐户,我的第一个问题是你真的需要一次显示它们吗? 这对用户真的有用吗?

如果没有,那么您可以继续使用第一种方法 – 只需将每页的帐户数限制在可接受的水平。 每个函数调用你仍然会做两个查询,但它可测试且可靠。

在您为报表打印输出页面的情况下,然后向用户简单解释可能需要一些时间。

我理解您需要更快的解决方案,但有时更快并不一定更加用户友好。

你最好的选择,假设你留在Rails中(而不是另一个工具)就是使用find_by_sql()。

它肯定会变得丑陋,但它将是可读的 – 并且不比原始SQL更丑陋。

我曾经在一些Rails应用程序上工作,其中“Ruby中的计算”被更高效的find_by_sql替换为报告。 它总是感觉有点脏,但我也喜欢拿5米报告,让它们在30秒内运行一些不错的SQL。