activerecord-import、Rails 標準のバルクインサート
activerecord-importの使用例と吐き出すSQL
code:zsh
❯ rails c
Loading development environment (Rails 7.0.3)
# 普通にやるとSQLがたくさん発行される
10.times do |i|
Book.create! name: "book #{i}" end
(1.0ms) SELECT sqlite_version(*)
TRANSACTION (0.0ms) begin transaction
TRANSACTION (0.5ms) commit transaction
TRANSACTION (0.0ms) begin transaction
TRANSACTION (0.3ms) commit transaction
TRANSACTION (0.0ms) begin transaction
TRANSACTION (0.3ms) commit transaction
TRANSACTION (0.0ms) begin transaction
TRANSACTION (0.3ms) commit transaction
TRANSACTION (0.0ms) begin transaction
TRANSACTION (0.5ms) commit transaction
TRANSACTION (0.0ms) begin transaction
TRANSACTION (0.3ms) commit transaction
TRANSACTION (0.0ms) begin transaction
TRANSACTION (0.3ms) commit transaction
TRANSACTION (0.0ms) begin transaction
TRANSACTION (0.3ms) commit transaction
TRANSACTION (0.0ms) begin transaction
TRANSACTION (0.2ms) commit transaction
TRANSACTION (0.0ms) begin transaction
TRANSACTION (0.3ms) commit transaction
=> 10
# activerecord-importを使う例
books = []
10.times do |i|
books << Book.new(name: "book #{i}") end
Book.import books
(0.1ms) SELECT sqlite_version(*)
TRANSACTION (0.1ms) begin transaction
Book Create Many (0.5ms) INSERT INTO "books" ("id","name","created_at","updated_at") VALUES (NULL,'book 0','2022-06-29 06:50:50.946518','2022-06-29 06:50:50.946518'),(NULL,'book 1','2022-06-29 06:50:50.946518','2022-06-29 06:50:50.946518'),(NULL,'book 2','2022-06-29 06:50:50.946518','2022-06-29 06:50:50.946518'),(NULL,'book 3','2022-06-29 06:50:50.946518','2022-06-29 06:50:50.946518'),(NULL,'book 4','2022-06-29 06:50:50.946518','2022-06-29 06:50:50.946518'),(NULL,'book 5','2022-06-29 06:50:50.946518','2022-06-29 06:50:50.946518'),(NULL,'book 6','2022-06-29 06:50:50.946518','2022-06-29 06:50:50.946518'),(NULL,'book 7','2022-06-29 06:50:50.946518','2022-06-29 06:50:50.946518'),(NULL,'book 8','2022-06-29 06:50:50.946518','2022-06-29 06:50:50.946518'),(NULL,'book 9','2022-06-29 06:50:50.946518','2022-06-29 06:50:50.946518')
TRANSACTION (0.9ms) commit transaction
=> #<struct ActiveRecord::Import::Result failed_instances=[], num_inserts=1, ids=[], results=[]> irb(main):009:0>
同じことをRails標準のバルクインサートでやるとどう書けるのか
code:rb
books = []
10.times do |n|
time = Time.current
books << { name: 'name', created_at: time, updated_at: time }
end
Book.insert_all books
irb(main):016:0> Book.insert_all books
(0.8ms) SELECT sqlite_version(*)
(0.1ms) SELECT sqlite_version(*)
Book Bulk Insert (2.8ms) INSERT INTO "books" ("name","created_at","updated_at") VALUES ('name', '2022-06-29 07:07:57.969759', '2022-06-29 07:07:57.969780'), ('name', '2022-06-29 07:07:57.969789', '2022-06-29 07:07:57.969794'), ('name', '2022-06-29 07:07:57.969798', '2022-06-29 07:07:57.969803'), ('name', '2022-06-29 07:07:57.969807', '2022-06-29 07:07:57.969811'), ('name', '2022-06-29 07:07:57.969816', '2022-06-29 07:07:57.969821'), ('name', '2022-06-29 07:07:57.969825', '2022-06-29 07:07:57.969829'), ('name', '2022-06-29 07:07:57.969834', '2022-06-29 07:07:57.969838'), ('name', '2022-06-29 07:07:57.969843', '2022-06-29 07:07:57.969847'), ('name', '2022-06-29 07:07:57.969851', '2022-06-29 07:07:57.969856'), ('name', '2022-06-29 07:07:57.969860', '2022-06-29 07:07:57.969864') ON CONFLICT DO NOTHING
activerecord-importでは配列を渡すだけでよかったが、Rails標準のメソッドだと1レコードとしての表現はハッシュになる。
insert_all, upsert_all expect an array of hashes, not ActiveRecord objects
簡単さでいえばactiverecord-importが上回るかな