如何用Rails ActiveRecord/PostgreSQL批量更新has_many关联记录的时间戳?
最优批量更新方案
你的当前方案性能极差:Table_B.all.map会把50万条记录全部加载到Ruby内存中,且每条update都会单独发送一条SQL请求,总共产生50万条SQL,既占用大量内存,又给数据库带来极大压力。
最优方案是直接用数据库级批量更新,仅需1条SQL语句完成所有操作,性能提升几个数量级。
分两种场景处理:
场景1:更新TableB的created_at/updated_at为关联TableA的对应字段(与你的代码逻辑一致)
Rails ActiveRecord写法
TableB.joins(:table_a).update_all( "table_bs.created_at = table_as.created_at", "table_bs.updated_at = table_as.updated_at" )
原生SQL写法(PostgreSQL)
UPDATE table_bs SET created_at = table_as.created_at, updated_at = table_as.updated_at FROM table_as WHERE table_bs.table_a_id = table_as.id;
原生SQL写法(MySQL)
UPDATE table_bs JOIN table_as ON table_bs.table_a_id = table_as.id SET table_bs.created_at = table_as.created_at, table_bs.updated_at = table_as.updated_at;
场景2:更新TableA的created_at/updated_at为关联TableB的对应字段(与你的文字描述一致)
注意:一个TableA对应多个TableB,需明确取哪条TableB的字段值,以下示例取每个TableA关联的最新TableB的时间:
Rails ActiveRecord写法
# 先查询每个TableA对应的最新TableB时间 subquery = TableB.select("table_a_id, MAX(created_at) as max_created_at, MAX(updated_at) as max_updated_at").group(:table_a_id) # 批量更新TableA TableA.joins("INNER JOIN (#{subquery.to_sql}) AS subquery ON table_as.id = subquery.table_a_id").update_all( "table_as.created_at = subquery.max_created_at", "table_as.updated_at = subquery.max_updated_at" )
原生SQL写法(PostgreSQL)
UPDATE table_as SET created_at = subquery.max_created_at, updated_at = subquery.max_updated_at FROM ( SELECT table_a_id, MAX(created_at) AS max_created_at, MAX(updated_at) AS max_updated_at FROM table_bs GROUP BY table_a_id ) AS subquery WHERE table_as.id = subquery.table_a_id;
额外优化建议
- 执行前务必备份数据,或在测试环境验证逻辑正确性
- 若仅需更新未同步的记录,可添加
WHERE条件减少更新行数,比如:WHERE table_bs.created_at != table_as.created_at - 大表更新建议避开业务高峰,或分批执行(若担心锁表)
内容的提问来源于stack exchange,提问作者Vinoth Kumar S
相关产品推荐
相关产品推荐

