我们需要生成报告,这些报告会提取大量数据,运行一些计算并将它们作为较大表的一部分进行吐出。这样做并不困难。但是,使它可以使用现有方法并且不生成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
然而,这也不是可读或易于测试。
你会如何解决它?
答案 0 :(得分:1)
假设你有1000个帐户,我的第一个问题是你真的需要一次显示它们吗?这对用户真的有用吗?
如果没有,那么您可以继续使用第一种方法 - 只需将每页的帐户数量限制在可接受的水平。每个函数调用仍然会执行两个查询,但它可测试且可靠。
如果您要为报表打印输出页面,那么向用户简单解释可能需要一些时间。
我理解您需要更快的解决方案,但有时更快并不一定更加用户友好。
答案 1 :(得分:1)
我参与了一些需要报告的项目。 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')
答案 2 :(得分:0)
你最好的选择,假设你留在Rails中(而不是另一个工具)只是使用find_by_sql()。
它肯定会变得丑陋,但它将是 READABLE - 并且不会比原始SQL更难看。
我参与了许多Rails应用程序,其中“Ruby中的计算”被更高效的find_by_sql替换为报告。它总是感觉有点脏,但我也喜欢拿着5米的报告,让它们在30秒内以一些不错的SQL运行。