0

我们需要生成报告来提取大量数据、运行一些计算并将它们作为更大表格的一部分输出。做到这一点并不难。但是,使现有方法可以使用而不生成 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

但是,这也不是很容易阅读或易于测试。

你会怎么解决?

4

3 回答 3

1

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

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

如果您正在为报告打印输出呈现页面,那么简单地向用户解释它可能需要一些时间。

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

于 2012-04-19T17:55:50.867 回答
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')
于 2012-04-28T09:21:50.560 回答
0

最好的选择,假设您留在 Rails 中(而不是其他工具),只需使用 find_by_sql()。

它肯定会很丑陋,但它会是可读的——而且不会比原始 SQL 更丑陋。

我已经在许多 Rails 应用程序中工作,其中“Ruby 中的计算”被替换为性能更高的 find_by_sql 专门用于报告。总感觉有点脏,但我也喜欢用一些像样的 SQL 来获取 500 万份报告并让它们在 30 秒内运行。

于 2012-04-19T17:50:58.547 回答