railsでbulk insertするときには今まではactiverecord-importが使うことが多かったと思いますが、
rails 6からはActiveRecord::Persistence#insert_all
が追加されたので標準機能を使ってbulk insertを行うことができるようになりました。
実際に使ってみたので使い方とかメモしておきます。
使い方
使い方は簡単でbulk insartを行うModelのclassを対象にinsert_all
を呼び出してあげるだけです。
class Entry < ApplicationRecord end now = Time.current Entry.insert_all([ { title: 'foo1', body: 'bar1', created_at: now, updated_at: now }, { title: 'foo2', body: 'bar3', created_at: now, updated_at: now }, ]) #=> Entry Bulk Insert (7.3ms) INSERT INTO "entries"("title","body","created_at","updated_at") VALUES ('foo1','bar1','2020-01-25 17:22:50.533182', '2020-01-25 17:22:50.533182'),('foo2','bar2','2020-01-25 17:22:50.533182', '2020-01-25 17:22:50.533182')
実行するとINSERT INTO table_name(column1, column2, ...) VALUES (value1, value2, ...)
といったSQLが生成されて実行されます。
このように一つのINSERT文にまとめられるので、DBアクセスが少なくすみパフォーマンスがよくなりそうです👍(重複レコードがあった場合にはinsertがskipされます。)
※created_at
とupdated_at
を補完してくれないので自分で設定しないといけない点に注意です。実装をシンプルにしてパフォーマンスを担保するのとupdate_all
の仕様に合わせているのが理由のようです🤔
https://github.com/rails/rails/issues/35493#issuecomment-470100313
ちなみにinsert_all!
、upsert_all
というメソッドも用意されていて、insert_all!
を使うとレコード重複が発生した際にActiveRecord::RecordNotUnique
をraiseします🚨upsert_all
を使うとレコードが重複が発生した場合に上書きされます💾(ON DUPLICATE KEY UPDATE
or ON CONFLICT
が実行される)
おわりに
rails 6から追加されたinsert_all
の使い方書いてみました。
activerecord-import
の方がエラーハンドリング周りとか良い感じにやってくれそうですが、シンプルなbulk insart機能をrails標準機能でシンプルに実装できるのは良いですね🙌