Rails 5.2中如何用单语句批量创建关联记录?
Rails 5.2.6.2 批量创建父级及关联子记录提速方案
一、用 activerecord-import 实现关联批量插入
这个gem支持批量插入并返回生成的ID,刚好能解决父-子关联的批量创建问题,步骤如下:
- 安装gem:
# 在Gemfile中添加 gem 'activerecord-import' # 执行bundle install
- 批量插入父记录并获取ID,再批量插入子记录:
# 构造父记录集合(跳过验证) parent_records = (1..1000).map { |i| Parent.new(name: "Parent #{i}") } # 批量插入父记录,指定returning: [:id]会自动填充实例的id属性 Parent.import parent_records, validate: false, returning: [:id] # 构造子记录集合,关联父ID child_records = [] parent_records.each_with_index do |parent, idx| 5.times do |j| child_records << Child.new( parent_id: parent.id, content: "Child #{idx}-#{j}", created_at: Time.current, updated_at: Time.current ) end end # 批量插入子记录 Child.import child_records, validate: false
二、自定义PostgreSQL SQL实现一次性批量插入
利用PostgreSQL的WITH子句和RETURNING语法,可以在单条SQL中完成父、子记录的批量插入,性能更优:
# 构造父记录的VALUES片段,注意用connection.quote做转义避免SQL注入 parent_values = (1..1000).map do |i| "(#{ActiveRecord::Base.connection.quote("Parent #{i}")}, NOW(), NOW())" end.join(',') # 构造完整SQL:先插入父记录,再用返回的ID插入子记录 sql = <<~SQL WITH inserted_parents AS ( INSERT INTO parents (name, created_at, updated_at) VALUES #{parent_values} RETURNING id ) INSERT INTO children (parent_id, content, created_at, updated_at) SELECT ip.id, 'Child ' || ip.id || '-' || gs.num, NOW(), NOW() FROM inserted_parents ip CROSS JOIN generate_series(1,5) gs(num) SQL # 执行SQL ActiveRecord::Base.connection.execute(sql)
注意事项
- 两种方案都跳过了模型验证,符合需求;如果需要部分验证,可调整
validate参数或手动处理 - 批量插入时需手动设置
created_at和updated_at(因为跳过了模型回调) - activerecord-import更贴合Rails开发习惯,维护成本低;自定义SQL性能略高,但需要手动处理字段和转义逻辑
内容的提问来源于stack exchange,提问作者C. Ball
相关产品推荐
相关产品推荐

