みなさん、こんにちは。まどぎわです(・∀・)
バッチ処理等で複数のレコードを一括で登録する際にどのようなコードを書いていますか?今回は、そんなときに便利なactiverecord-import
の使い方を調べたのでメモしておきますφ(..)
activerecord-import
の使い方
改善対象のコード例
複数のレコードを取得して登録する処理でパッと思いつくのは下記のようなコードでしょうか?
class NewBook < ApplicationRecord
def self.make_new_books
books = Book.where('created_at >= ?', Time.current.beginning_of_month)
books.each do |book|
NewBook.create(book_id: book.id)
end
end
end
しかし、これを実行すると下記のようにレコード数分のINSERTを行うSQLが発行され、効率がよくありません。。。
(0.1ms) begin transaction
SQL (1.5ms) INSERT INTO "new_books" ("book_id", "created_at", "updated_at") VALUES (?, ?, ?) [["book_id", 2], ["created_at", "2018-04-21 08:41:30.705444"], ["updated_at", "2018-04-21 08:41:30.705444"]]
(1.0ms) commit transaction
(0.1ms) begin transaction
SQL (0.7ms) INSERT INTO "new_books" ("book_id", "created_at", "updated_at") VALUES (?, ?, ?) [["book_id", 3], ["created_at", "2018-04-21 08:41:30.710686"], ["updated_at", "2018-04-21 08:41:30.710686"]]
(1.1ms) commit transaction
(0.1ms) begin transaction
SQL (0.5ms) INSERT INTO "new_books" ("book_id", "created_at", "updated_at") VALUES (?, ?, ?) [["book_id", 6], ["created_at", "2018-04-21 08:41:30.715154"], ["updated_at", "2018-04-21 08:41:30.715154"]]
(0.9ms) commit transaction
以下省略
activerecord-import
を使う準備
こんな時に便利なのが、activerecord-import
というGemです!
github.com
使い方は、簡単でGemfile
に下記を追記してbundle install
を実行するだけです。
gem 'activerecord-import'
activerecord-import
を使ってコードを修正してみる
上で書いたコードをactiverecord-import
で書き直すとこんな感じ。
class NewBook < ActiveRecord::Base
def self.make_new_books
destroy_all
start_at = Time.current.iso8601(2)
book_ids = Book.where('created_at >= ?', Time.current.beginning_of_month).pluck(:id)
NewBook.import book_ids.map{ |book_id| NewBook.new(book_id: book_id) }
end_at = Time.current.iso8601(2)
end
end
実行されるSQLは下記のような感じです。INSERT文が一つになっていて効率的ですね(・∀・)
Class Create Many Without Validations Or Callbacks (2.5ms) INSERT INTO "new_books" ("id","book_id","created_at","updated_at") VALUES (NULL,2,'2018-04-21 14:04:17.913385','2018-04-21 14:04:17.913529'),(NULL,3,'2018-04-21 14:04:17.913385','2018-04-21 14:04:17.913529'),(NULL,6,'2018-04-21 14:04:17.913385','2018-04-21 14:04:17.913529'),(NULL,7,'2018-04-21 14:04:17.913385','2018-04-21 14:04:17.913529'),(NULL,8,'2018-04-21 14:04:17.913385','2018-04-21 14:04:17.913529'),(NULL,9,'2018-04-21 14:04:17.913385','2018-04-21 14:04:17.913529'),(NULL,10,'2018-04-21 14:04:17.913385','2018-04-21 14:04:17.913529')
レコード件数は、7件とかなりイマイチですが、、、ベンチマークをとってみました。
case |
start_at |
end_at |
time |
nomal |
2018-04-21T14:09:51.75 |
2018-04-21T14:09:51.82 |
0.7s |
user import |
2018-04-21T14:10:29.37 |
2018-04-21T14:10:29.41 |
0.4s |
これぐらいの件数でも意外と差がでますね!(・∀・)
やはりactiverecord-import
の方が効率が良いみたいです!
おまけundefined method
import' for Model`と出る場合
良くわからないけど、rubyのバージョンを2.4.0
から2.4.3
に変更したら治った・・・。
おわりに
夜間バッチでランキングを作ったりと実際の業務だと一括登録の処理は実装する機会が多そうですね!
一括登録処理は結構重くなりがちになりそうなので、activerecord-import
を使って効率的な処理で行えるようにしたいですね(・∀・)