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

Ruby on rails 使用ActiveRecord更新新表中的所有记录,ruby-on-rails,activerecord,Ruby On Rails,Activerecord,我有一张20000多行的桌子。我在统计栏中记录每个客户在广告活动中的每日统计数据。如果我已经在单独的列中计算了转换和花费的总数,那么调用某些请求的数据会更容易,所以我已经添加了它们 Google_记录模式- create_table "google_records", :force => true do |t| t.string "user_id" t.string "date" t.text "stats" t.text "account_name" t

我有一张20000多行的桌子。我在统计栏中记录每个客户在广告活动中的每日统计数据。如果我已经在单独的列中计算了转换和花费的总数,那么调用某些请求的数据会更容易,所以我已经添加了它们

Google_记录模式-

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"
随着记录的创建,这些列将被填充。但是,我正在尝试找出更新表中已有记录的最佳方法

Stats是一个散列,我可以得到一条记录的总数,如下所示:

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\u all的不当使用)

我在概念上走对了方向吗

更新***

在尝试实施建议时,我意识到并非所有用户都有这些统计数据。如果我能让它只为一个用户工作,我就能为所有有统计数据的用户重写它。我相信我很快就能让它为一个用户工作了,但我不知道我把每个模块都搞砸了

以下是我所拥有的:

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\u cost
结果插入
:total\u cost
,但在重写几次后,我无法得到它,因此每个单独的
total\u cost
计算都会进入相应的
:total\u 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'

您可以查看api文档


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

# 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
#迁移中的代码
类HandleGoogleRecordsToUserstotalcost)
结束
结束
结束
降下
raise ActiveRecord::不可逆转迁移.new“不允许向下迁移。”
结束
def总成本(谷歌记录)
#如果散列中的数据不正确,您可以将其写入begin and rescue以返回0
结果=0
开始
result=google|record.map{m | m.stats}.map{s | s.map{key,value | value[:cost]| | 0}.map{m | m.inject(:+)}.compact.reduce(&:+)
救援=>e
Rails.logger.error(“google#u record#{google_record.inspect}不计算总成本”)
结果=0
结束
结果
结束
结束

最好为此执行rake任务,因为您可能希望在将来某个时候重做它(例如,如果您添加更多数据缓存列,或者如果总数与其余数据不同步)。@MaxWilliams-我同意。这是我的计划,但我想在设置rake任务之前确保它正常运行,这就是我遇到的问题-我无法让它更新dev env中只有200行的数据集。谢谢@cenyong-因为我需要在每个数据集运行时计算其总数(
totalcost
),您是否建议在您的行和每个块中运行
.each
计算
totalcost
,如我在问题中所述?我建议更改您的模式,并将每个stat记录存储在db中,而不是将其存储为散列字符串。之后,您可以使用sql的sum和GROUPBY来完成这项工作,这将更加高效。
GoogleRecord.update_all(['total_cost = ?', totalcost], ['user_id = ? and date = ?', user.id, d])
# 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