ActiveRecord でbulk insert/update

mala さんのMySQLにおけるbulk insert と bulk update - 金利0無利息キャッシング – キャッシングできます - subtechのエントリーを見て, MySQL でbulk update ができることを知り, 丁度欲しい機能だったのでとりあえず動くだけのメソッドを実装.

# User.extend BulkExecuteMethods
# User.update_multi([:id, :name, :age], [[1, 'bob', 11], [2, 'mary', 21]])
#
# refs
#  http://dev.mysql.com/doc/refman/4.1/ja/insert.html
#  http://subtech.g.hatena.ne.jp/mala/20090729/1248880239
module BulkExecuteMethods
  def update_multi(columns, values)
    cols = columns.map{|c| self.connection.quote_column_name(c) }

    ph = (['(?)'] * values.size).join(',')
    vals = sanitize_sql([ph, *values])
    
    expr = cols.map{|c| "#{c}=VALUES(#{c})" }.join(',')
    
    sql = sprintf(<<-SQL, self.table_name, cols.join(','), vals, expr)
INSERT INTO %s (%s) VALUES %s ON DUPLICATE KEY UPDATE %s
SQL
    
    affected_rows = self.connection.update(sql)
    
    # number of updated
    # original affected rows = number of inserted + number of duplicated(updated)
    # if no error occured, number of inserted equals number of duplicated(updated)
    affected_rows / 2
  end
end
  • validation 通さない
  • 存在しないid を指定してしまうとinsert されてしまう上, それを特に検知しない
  • update した数の算出が適当


ここからまともにしていくかと思いきや, gem があった.