Rails6.0では、bulk insert用にinsert_all
insert_all!
upsert_all
という3つのメソッドがActiveRecordに追加されました。
Rails5.2までのbulk insertはactiverecord-importというGemを使うのが主流でしたが、それに置き換わるものになりそうです。
1. insert_all
メソッド
insert_all
メソッドは、その名の通り複数件のレコードを一括でINSERTします。
def insert_all(attributes, returning: nil, unique_by: nil)
InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by).execute
end
on_duplicate: :skip
となっているため、id
(主キー)が重複しているレコードはスキップされます。
例えば
User.insert_all([
{ id: 1, name: 'Taro', age: 20 },
{ id: 1, name: 'Jiro', age: 21 }
])
とやると、2件目の{ id: 1, name: 'Jiro', age: 21 }
は無視されて、INSERTされません。
1-1. returning
オプション
returning
は、PostgreSQLにのみ有効なオプションです。
返り値として得られる、INSERTに成功したレコードの属性を指定することができます。
デフォルトでは、id
(主キー)が返って来ます。
例えば
returning: %w[ id name ]
と指定すると、id
とname
が返ってきます。
1-2. unique_by
オプション
unique_by
は、PostgreSQLとSQLiteにのみ有効なオプションです。
デフォルトでは、id
(主キー)のみが重複しているとスキップされます。unique_by
オプションをつけると、重複でスキップするカラムを配列によって追加で指定できます。
unique_by: { columns: %w[ isbn ] }
以下のようにcolumns:
とwhere:
を組み合わせることで、部分インデックスを指定することもできます。
unique_by: { columns: %w[ isbn ], where: "published_on IS NOT NULL" }
2. insert_all!
メソッド
insert_all!
メソッドは、insert_all
メソッドと同様に複数件のレコードを一括でINSERTします。
insert_all
メソッドとの違いは、id(主キー)が重複しているとスキップするのではなくエラーを返します。
def insert_all!(attributes, returning: nil)
InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning).execute
end
on_duplicate: :raise
となっているため、id(主キー)が重複しているとraise ActiveRecord::RecordNotUnique
のエラーを返します。
例えば
User.insert_all!([
{ id: 1, name: 'Taro', age: 20 },
{ id: 1, name: 'Jiro', age: 21 }
])
とやると、2件目の{ id: 1, name: 'Jiro', age: 21 }
でraise ActiveRecord::RecordNotUnique
のエラーが発生します。
2-1. returning
オプション
1-1.returning
オプションと同じです。
3. upsert_all
メソッド
upsert_all
メソッドは、まだ存在しないレコードならINSERT、既に存在するレコードならUPDATEします。
def upsert_all(attributes, returning: nil, unique_by: nil)
InsertAll.new(self, attributes, on_duplicate: :update, returning: returning, unique_by: unique_by).execute
end
on_duplicate: :update
となっているため、id(主キー)が重複しているとupdateします。
3-1. returning
オプション
1-1. returning
オプションと同じです。
3-2. unique_by
オプション
1-2. unique_by
オプションと同じです。