使用ActiveRecord更新新表中的所有记录

时间:2014-04-07 15:11:16

标签: ruby-on-rails activerecord

我有一张超过20,000行的表格。我会在统计信息列中记录每个客户的广告系列每日统计信息。如果我已经在单独的列中计算了转化和支出的总数,那么为某些请求调出数据会更容易,所以我已经添加了它们。

Google_Records架构 -

create_table "google_records", :force => true do |t|
 t.string   "user_id"
 t.string   "date"
 t.text     "stats"
 t.text     "account_name"
 t.datetime "created_at",                                                        :null => false
 t.datetime "updated_at",                                                        :null => false
 t.decimal  "total_cost",        :precision => 12, :scale => 2, :default => 0.0, :null => false
 t.integer  "total_conversions",                                :default => 0,   :null => false
end

add_index "google_records", ["date"], :name => "index_google_records_on_date"

在创建记录时向前移动将填充这些列。但是,我试图找出更新表中已有记录的最佳方法。

统计数据是一个哈希值,我可以像这样得到一条记录的总数:

user = User.find(6)
GoogleRecord.where(user_id: user).where(date: "20140328").map {|m| m.stats}.map{ |s| s.map{ |key,        value| value[:cost] || 0 } }.map {|m| m.inject(:+)}.compact.reduce(&:+)
=> 660.26

我目前正在考虑编写可以一次更新所有表的迁移。这可能吗?

我甚至无法让代码为一个用户正确执行(尽管我觉得这可能是对update_all的错误使用)

("20140320".."20140323").each do |d|
  user = User.find(6)
  totalcost = GoogleRecord.where(user_id: user).where(date: d).map {|m| m.stats}.map{ |s| s.map{ |key, value| value[:cost] || 0 } }.map {|m| m.inject(:+)}.compact.reduce(&:+)
  GoogleRecord.where(user_id: user).where(date: d).update_all(:total_cost => totalcost)
end

我是否在概念上走向正确的方向?

更新***

在尝试实施建议时,我意识到并非所有用户都拥有这些统计信息。如果我能让它只为一个用户工作,我将能够为所有拥有统计数据的用户重写它。我相信我已经接近让它为一个用户工作,但我无法弄清楚我在哪里弄乱了每个区块。

这就是我所拥有的:

user = User.find(6)
("20140320".."20140323").each do |d|
google_record = GoogleRecord.where(user_id: user).where(date: d)
total_cost = google_record.map {|m| m.stats.map{ |key, value| value[:cost] || 0 }}.compact.reduce(&:+) || 0
google_record.update_all(:total_cost => total_cost)
end

我现在收到以下错误,表示此块正在尝试插入所有记录的结果' total_cost :total_cost进入total_cost,但经过几次重写后,我无法获得该结果,以便每个单独的:total_cost计算进入相应的←[1m←[36mGoogleRecord Load (2.0ms)←[0m ←[1mSELECT "google_records".* FROM "google_records" WHERE "google_records"."user_id" = 6 AND "google_records"."date" = '20140320'←[0m ←[1m←[35mSQL (2.0ms)←[0m UPDATE "google_records" SET "total_cost" = 258.92,35.82,18.58,47.78,1.42,0.0,0.0,0.0,0.0,0.0,0.44,82.07,2.19,5.87,0.0,0.0 WHERE "google_records"."user_id" = 6 AND "google_records"."date" = '20140320' ActiveRecord::StatementInvalid: SQLite3::SQLException: near "35.82": syntax error: UPDATE "google_records" SET "total_cost" = 258.92,35.82,18.58,47.78,1.42,0.0,0.0,0.0,0.0,0.0,0.44,82.07,2.19,5.87,0.0,0.0 WHERE "google_records"."user_id" = 6 AND "google_records"."date" = '20140320'

{{1}}

2 个答案:

答案 0 :(得分:1)

你可以看看api doc。 http://apidock.com/rails/ActiveRecord/Base/update_all/class

GoogleRecord.update_all(['total_cost = ?', totalcost], ['user_id = ? and date = ?', user.id, d])

答案 1 :(得分:0)

我认为正确的方法是将其写入迁移脚本。我编辑你的代码和迁移脚本可能如下所示:

# code in migration

class HandleGoogleRecordsToUsers < ActiveRecord::Migration

    def up
        User.find_each do |user|
            ("20140320".."20140323").each do |d|
                google_record = GoogleRecord.where(user_id: user).where(date: d)
                totalcost = total_cost(google_record)
                google_record.update_all(:total_cost => totalcost)
            end
        end 
    end

    def down
        raise ActiveRecord::IrreversibleMigration.new "Dont allowed to migrate down."
    end

    def total_cost(google_record)
        # you can write it inside begin and rescue to return 0 if data in hash was incorrect
        result = 0
        begin
            result = google_record.map {|m| m.stats}.map{ |s| s.map{ |key, value| value[:cost] || 0 } }.map {|m| m.inject(:+)}.compact.reduce(&:+)
        rescue => e
            Rails.logger.error("google_record #{google_record.inspect} doesnt count total cost")
            result = 0  
        end
        result
    end

end