Ruby on rails 使用ActiveRecord更新新表中的所有记录
我有一张20000多行的桌子。我在统计栏中记录每个客户在广告活动中的每日统计数据。如果我已经在单独的列中计算了转换和花费的总数,那么调用某些请求的数据会更容易,所以我已经添加了它们 Google_记录模式-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
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