0

大量のデータを取り込み、いくつかの計算を実行し、それらを大きなテーブルの一部として吐き出すレポートを生成する必要があります。これを行うことは難しくありません。ただし、既存のメソッドを使用できるようにし、何千もの 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 で計算したい場合に必要なすべてのデータを取得しますが、Ruby でbalance_atすべての数値計算を実行するのは私の素敵な小さなメソッドではありません (個々のケースでは遅くなります)。

キャッシュされているものに応じて、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

私は報告が必要ないくつかのプロジェクトに取り組んできました。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 に答える
1

何千ものアカウントを持っていると仮定すると、私の最初の質問は、それらをすべて一度に表示する必要があるかということです。これはユーザーにとって本当に便利ですか?

そうでない場合は、最初の方法を続行できます。ページあたりのアカウント数を許容レベルに制限するだけです。関数呼び出しごとに 2 つのクエリを実行することになりますが、テスト可能で信頼性があります。

レポートの印刷用にページをレンダリングしている場合は、時間がかかる場合があることをユーザーに簡単に説明します。

より高速なソリューションが必要であることは理解していますが、高速であることが必ずしもユーザー フレンドリーであるとは限りません。

于 2012-04-19T17:55:50.867 に答える
0

(別のツールとは対照的に) Rails 内にとどまっていると仮定すると、最善の策は単に find_by_sql() を使用することです。

それは確かに醜いものになるでしょうが、それは読みやすいでしょう- そして生のSQLよりも醜いものではありません。

私は多くのRailsアプリに取り組んできました.「Rubyでの計算」は、具体的にはレポート用のはるかにパフォーマンスの高いfind_by_sqlに置き換えられました. いつも少し汚い感じがしますが、私は 5m のレポートを取得し、適切な SQL を使用して 30 秒で実行するのも好きです。

于 2012-04-19T17:50:58.547 に答える