Rails(ActiveRecord)でBULK INSERTする方法

はじめに


RailsでRake Taskなんか作ったときは、バッチ処理するようなことを書くと思います。
多分ですが・・・

この前、バルクインサートをすることがあったのでやり方を残しておきます。

因みに、普通にrubyスクリプトの中でも使うこともできると思います(これも多分w)

やり方


activerecord-importというGemを使用します。

Gemfileに以下を追加すれば、Rake Taskで使用することができます。
ぶっちゃけRake Taskだけではなく、普通に使うこともできますが・・・

1
gem 'activerecord-import'

利用できるデータベースは以下のものです。

  • mysql(アダプターはmysql, mysql2の両方で使用可能)
  • postgresql
  • sqlite3

前提


かなり簡単です。

以下のようなモデルを用意したとします。

  • モデル名:Hoge
  • カラム(主キー): id
  • カラム(名前): name
  • カラム(テキスト): text

このモデルを10個まとめてバルクインサートしたいとします。

やり方


かなり簡単です。。。

  1. バルクインサートしたい数だけモデルオブジェクトを格納したリストを用意する
  2. 該当のモデルのimportメソッドに1で作成したリストを渡す

コードに起こすとこんな感じです。。。

1
2
3
4
5
6
hoge_list = []
10.times do |i|
  hoge_list << Hoge.new(id: i, name: "hoge #{i}", text: "fugafuga")
end

Hoge.import hoge_list

実行するとこんな感じのSQLが一気に発行されます。。。

1
2
INSERT INTO `hoge` (`id`,`name`,`text`,`created_at`,`updated_at`) VALUES (1,'hoge 1','fugafuga','2013-11-25 00:30:30','2013-11-25 00:30:30') ON DUPLICATE KEY UPDATE `messages`.`updated_at`=VALUES(`updated_at`)
INSERT INTO `hoge` (`id`,`name`,`text`,`created_at`,`updated_at`) VALUES (2,'hoge 2','fugafuga','2013-11-25 00:30:30','2013-11-25 00:30:30') ON DUPLICATE KEY UPDATE `messages`.`updated_at`=VALUES(`updated_at`)

因みに、以下のようなオプションがあります。 利用する場合、importメソッドの引数にハッシュで指定するだけです。

  • 「:on_duplicate_key_update」: ユニークキーが重複したカラムを更新したい場合に設定
  • 「:timestamps」: falseを設定すると、自分でcreated_at,created_on,update_at,update_onを設定
  • 「:validate」: falseを設定すると、モデル検証をスキップする(デフォルトはtrue)

こんな感じで設定します。。。

1
2
columns = [:id, :name, :text]
Hoge.import hoge_list, :on_duplicate_key_update => columns, :timestamps => false, :validate => false

使用感


結構、問題ない感じで使えました。。。

自分が使用した感じだと10万件の更新に1分程度かかったので、
データが増えていくようなモデル(テーブル)に対して、多用するのは危険かなと思いました。

コードベースで、バルクインサートやバルクアップデートができるところがいいですね。
データをまとめて引っ張って→データ処理→まとめて更新なんかの使い方では便利かもw