0

私はSleepモデルを持っています。インスタンスはbelongs_toで、Personモデルのインスタンスです。統計の計算をバックグラウンドスレッドに渡したい。人々は自分のデータを自己報告し、数日をスキップすることがあります。Rails 3.1のクエリをより効率的にするにはどうすればよいですか?

私はSleepstatモデルを作成し、Sleepの1つ以上の記録されたインスタンスがある毎日の統計を計算する予定です。しかし、このバックグラウンドタスクでは、Sleepstatという既存のインスタンスをスキャンして、needs_updatingフラグのステータスを調べる必要があります。

誰かがSleepstatが存在しない日にSleepレコードを作成した場合、バックグラウンドタスクでSleepstatを作成し、その日の統計を計算します。もし誰かがのある日に追加のSleepレコードを追加した場合は、最新の統計情報を最新の状態に保つために、必要に応じてSleepstatにフラグを立てて、新しいデータで更新します。

  1. が質問にPersonに属するすべてのSleepレコードを返すために、クエリを実行します。

    私の考えは次の操作を実行することでした。 、days_recordedのそれぞれについて

    days_recorded = [] 
        for sleep in all_sleeps 
        days_recorded.push sleep.start_time.to_date 
        end 
        days_recorded = days_recorded.uniq 
    
  2. かどうかを確認します:

    all_sleeps = Sleep.select('start_time,end_time,multiday,time_zone,in_progress').where(:person_id => self.id) 
    
  3. がユニークstart_time日付の配列を作成します。そのために、私は私が期待するように動作し、このクエリを使用しましたSleepstatが存在します。そうでない場合は、作成して統計を計算します。その場合は、needs_updatingかどうかを確認してください。その場合は、統計を計算します。そうでない場合は、days_recordedの次の項目に進みます。

    days_recorded.each do |d| 
        stat = Sleepstat.where(:date => d).first 
    
        if stat.nil? 
         # No record, so create one because we have data for that day and calculate stats 
         ... 
    
        else 
         # There is a record. Evaluate whether it needs to be updated 
    
         if stat.needs_updating? 
         # Update the Sleepstat 
         ... 
    
         end 
    
        end 
    end 
    

このアプローチは、独立したクエリの多くにつながる:

existing_stats = Sleepstat.where(:date => days_recorded) 

Sleepstat Load (0.2ms) SELECT "sleepstats".* FROM "sleepstats" WHERE "sleepstats"."date" = '2011-12-10' 
Sleepstat Load (0.2ms) SELECT "sleepstats".* FROM "sleepstats" WHERE "sleepstats"."date" = '2011-12-11' 
Sleepstat Load (0.2ms) SELECT "sleepstats".* FROM "sleepstats" WHERE "sleepstats"."date" = '2011-12-12' 
Sleepstat Load (0.2ms) SELECT "sleepstats".* FROM "sleepstats" WHERE "sleepstats"."date" = '2011-12-13' 

私の考えはのようなクエリを通じて、最初Sleepstat秒のすべてをつかむしようとしました

と入力し、手順3で繰り返します。私の試行は次のようになります。

私は何度データベースを打っていないよよう

Sleepstat Load (0.5ms) SELECT "sleepstats".* FROM "sleepstats" WHERE "sleepstats"."date" IN ('2011-12-07', '2011-12-06', '2011-12-08', '2011-12-09', '2011-12-10', '2011-12-11', '2011-12-12', '2011-12-13', '2011-12-14', '2011-12-15') AND "sleepstats"."date" = '2011-12-10' 
Sleepstat Load (0.9ms) SELECT "sleepstats".* FROM "sleepstats" WHERE "sleepstats"."date" IN ('2011-12-07', '2011-12-06', '2011-12-08', '2011-12-09', '2011-12-10', '2011-12-11', '2011-12-12', '2011-12-13', '2011-12-14', '2011-12-15') AND "sleepstats"."date" = '2011-12-11' 
Sleepstat Load (0.6ms) SELECT "sleepstats".* FROM "sleepstats" WHERE "sleepstats"."date" IN ('2011-12-07', '2011-12-06', '2011-12-08', '2011-12-09', '2011-12-10', '2011-12-11', '2011-12-12', '2011-12-13', '2011-12-14', '2011-12-15') AND "sleepstats"."date" = '2011-12-12' 
Sleepstat Load (0.4ms) SELECT "sleepstats".* FROM "sleepstats" WHERE "sleepstats"."date" IN ('2011-12-07', '2011-12-06', '2011-12-08', '2011-12-09', '2011-12-10', '2011-12-11', '2011-12-12', '2011-12-13', '2011-12-14', '2011-12-15') AND "sleepstats"."date" = '2011-12-13' 

がどのように私は、このプロセスの効率を向上させることができます:

existing_stats = Sleepstat.where(:date => days_recorded) 

    days_recorded.each do |d| 
     stat = existing_stats.where(:date => d) 

     if stat.nil? || stat.length == 0 
      # No record, so create one because we have data for that day and calculate stats 
      ... 

     else 
      # There is a record. Evaluate whether it needs to be updated 

      if stat.needs_updating? 
      # Update the Sleepstat 
      ... 

      end 

     end 
    end 

これは単に、より複雑な個々のクエリの多くにつながりましたか?

答えて

1

あなたの統計情報を計算することが重すぎていない場合、あなたはより良い統計にレコードが作成または更新されるたびに計算するcallback を使用する必要があります。もちろん

Class Sleep < ActiveRecord::Base 
    before_save :create_or_update_stats 

    def create_or_update_stats 
    # avoid calculation if record is new or if nothing changed 
    return unless (self.new_record? || self.changed?) 

    date = self.start_time.to_date 
    stats = Sleepstat.find_or_create_by_date(date) 
    sleeps = Sleep.where(date: date) 

    # now calculate the stats and save them. 
    end 
end 

EDIT、あなた'LL破壊時にもコールバックを追加する必要があります。あなたは精神を得る。

余分なヒント:

  • for構文を使用しないでください。それは内部的にeachを呼び出します、なぜそんなに気にしますか?
  • これはあなたが2で行うのと同じもの)が行われます、あなたは本当に重い計算を行う必要がある場合は代わりにあなたの声明

  • stat.exists?を使用し、関係が空であるかどうかをテストするために

    all_sleeps.map{|s| s.start_date.to_date }.uniq 
    # or even this 
    all_sleeps.map(&:start_date).map(&:to_date).uniq 
    
+0

「sleeps = Sleep.where(date:date)」に関する質問は、arel tablesを見てください。これにより、レコードのインスタンシエーションをスキップして必要なデータのみを読み込むことができます。 '...' Sleep'は、私が日付を評価するために使用しているフィールド 'start_time'を持っていますので、タイムスタンプから日付にキャストする必要があります。しかし、それはUTCに保存されています。どのように日付にキャストし、タイムゾーン情報を保持することができますか? self_id、the_date) '(ps)を使用しています。(sleep_on_date = Sleep.select( 'start_time、end_time、multi_day、time_zone、in_progress' ) – Clay

+0

mmmm。時間を扱うことは常に難しい(btw、間違いのために申し訳ありません)。特に、サイトの日付/時刻を入力すると、指定した時刻がタイムゾーンにあると予想されるためです。クライアントのタイムゾーンを検出してフィールドをUTCに変換し、おそらくクライアントのタイムゾーンを検出してそれを元に戻すことをお勧めします。一方で、それは過ちかもしれません...デートのために。何人の人が、別のタイムゾーンで日付+ 1と効果的に一致するDateTimeを入力しますか? –

+0

あなたの問題は夜を中心に集中しています。あなたはすでに、「睡眠」が人々が眠ったと感じる日にリンクされることを選択しました。これは設計上の問題です...すべてのことを切り上げました。あなたの日付を3つの整数列(日、月、年)にインデックスを付けて保存してみませんか?あなたがパフォーマンスを求めているなら、それは私が考える方法です –