批量将记录插入Active Record表

时间:2013-03-10 01:17:11

标签: ruby-on-rails activerecord bulkinsert

我发现当我一次添加大量记录时,我的Model.create!语句需要很长时间才能运行。看看ActiveRecord-Import,但它不适用于哈希数组(这是我所拥有的,我认为这很常见)。如何提高性能?

6 个答案:

答案 0 :(得分:22)

使用activerecord-import gem。我们假设您正在读取CSV文件并生成Product目录,并且您希望以1000个批次插入记录:

batch,batch_size = [], 1_000 
CSV.foreach("/data/new_products.csv", :headers => true) do |row|
  batch << Product.new(row)

  if batch.size >= batch_size
    Product.import batch
    batch = []
  end
end
Product.import batch

答案 1 :(得分:10)

感谢Chris Heald @cheald的2009 article,并向我展示了最好的方法是多行插入命令。

在我的initializers/active_record.rb文件中添加了以下代码,将我的Model.create!(...)电话更改为Model.import!(...),然后将其删除。一些警告:

1)它不验证数据 2)它使用SQL INSERT命令的形式,其内容类似于......

INSERT INTO <table> (field-1, field-2, ...) 
       VALUES (value-1-1, value-1-2, ...), (value-2-1, value-2-2, ...), ...`

...这可能不是所有数据库的正确语法,但它适用于Postgres。为SQL版本更改适当语法的代码并不困难。

在我的特殊情况下,将19K +记录插入我的开发机器(带有8GB RAM,2.4GHz Intel Core i5和SSD的MacBook Pro)上的简单表中,使用'model.create!'从223秒开始。使用'model.import!'到7.2秒。

class ActiveRecord::Base

  def self.import!(record_list)
    raise ArgumentError "record_list not an Array of Hashes" unless record_list.is_a?(Array) && record_list.all? {|rec| rec.is_a? Hash }
    key_list, value_list = convert_record_list(record_list)        
    sql = "INSERT INTO #{self.table_name} (#{key_list.join(", ")}) VALUES #{value_list.map {|rec| "(#{rec.join(", ")})" }.join(" ,")}"
    self.connection.insert_sql(sql)
  end

  def self.convert_record_list(record_list)
    key_list = record_list.map(&:keys).flatten.uniq.sort

    value_list = record_list.map do |rec|
      list = []
      key_list.each {|key| list <<  ActiveRecord::Base.connection.quote(rec[key]) }
      list
    end

    return [key_list, value_list]
  end
end

答案 2 :(得分:5)

我开始遇到大量记录(&gt; 10000)的问题,因此我修改了代码,以便一次以1000个记录为一组进行操作。以下是新代码的链接:

https://gist.github.com/jackrg/76ade1724bd816292e4e

答案 3 :(得分:1)

您还可以使用activerecord-insert_many gem。只需制作一个对象数组!

events = [{name: "Movie Night, time: "10:00"}, {name: "Tutoring", time: "7:00"}, ...]

Event.insert_many(events)

答案 4 :(得分:0)

使用交易加速批量插入很多!

Model.transaction do
    many.times{ Model.create! }
end

如果涉及多个模型,请对受影响的每个模型执行Model.transaction:

Model1.transaction do
    Model2.transaction do
        many.times do
            m1 = Model1.create!
            m1.add_model2
        end
    end
end

答案 5 :(得分:0)

对于 Rails 6.x,使用 insert_all