我有一张超过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}}
答案 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