4

Rails 3.1、Ruby 1.9.2、AR/MySQL。

同じタイプの結果がその期間中に多くの結果を持っている場合、期間 (日) ごとに 1 つの結果のみを保持する方法についての提案を探しています。例として、株価の追跡があります。最初は、15 分ごとに価格を保存しますが、すべての価格ポイントを 1 週間保存するだけで済みます。最初の 1 週間以降は、1 日あたり 1 つの価格 (最終記録、終値) のみが必要になります。

これは簡単な最初の試みで、うまくいきますが、非常に非効率的です:

# stock has many prices, price has one stock
# get all prices for single stock older than 1 week
prices = stock.prices.where("created_at < ? ", Time.now-1.week)  
prices.group_by{ |price| price.created_at.to_date }.each do |k,v| # group by day
  if v.count > 1  # if many price points that day
    (v[0]..v[v.size-2]).each {|r| r.delete} # delete all but last record in day
  end
end

ヘルプ/提案をお寄せいただきありがとうございます。誰かの助けになることを期待して、作業を進めながら更新しようと思います。

4

2 に答える 2

3

すべてを SQL で実行し、範囲を最後に実行された時間に制限することで、はるかに効率的にすることができます。また、古い一日の終わりのエントリを「アーカイブ済み」としてマークする列を追加すると、クエリがはるかに簡単になります。アーカイブされた価格は、1 週間後に削除されない価格です。

rails generate migration add_archived_to_prices archived:boolean

移行する前に、created_at 列のインデックスに移行を変更します。

class AddArchivedToPrices < ActiveRecord::Migration
  def self.up
    add_column :prices, :archived, :boolean
    add_index :prices, :created_at
  end

  def self.down
    remove_index :prices, :created_at
    remove_column :prices, :archived
  end
end

ワークフローは次のようになります。

# Find the last entry for each day for each stock using SQL (more efficient than finding these in Ruby)
keepers =
  Price.group('stock_id, DATE(created_at)').
        having('created_at = MAX(created_at)').
        select(:id).
        where('created_at > ?', last_run) # Keep track of the last run time to speed up subsequent runs

# Mark them as archived
Price.where('id IN (?)', keepers.map(&:id)).update_all(:archived => true)

# Delete everything but archived prices that are older than a week
Price.where('archived != ?', true).
      where('created_at < ?", Time.now - 1.week).
      where('created_at > ?', last_run). # Keep track of the last run time to speed up subsequent runs
      delete_all

group()最後に、とを組み合わせないでくださいupdate_all()group()で無視されupdate_all()ます。

于 2012-04-30T03:37:12.503 に答える
1

各 like で delete を呼び出す代わりに

 (v[0]..v[v.size-2]).each {|r| r.delete}

delete_all を実行しますが、最後のものは実行しません

price_ids_to_keep = []
if v.count > 1  # if many price points that day
  price_ids_to_keep << v[-1].id # get the last
else
  price_ids_to_keep << v[0].id
end

prices.where('id not in (?)',price_ids_to_keep).delete_all

私はこれをやったことがありませんが、うまくいくはずだと確信しています


これは、DELETE クエリが少なくなるため優れていますが、これらすべてを 1 つの大きなクエリで行う方法が必要です。


ビジネスの目で、あなたまたはあなたのチームはそれについてもっとよく考えるべきです。現在、ストレージは安価であり、このような情報は、将来のデータ マイニングなどにとって貴重なものになる可能性があります。

于 2012-04-30T02:49:14.563 に答える