ActiveRecordのupsert_all
概要
upsert_all / upsert_all!
複数のレコードを単一のSQLクエリで挿入または更新する(バルクアップサート)
重複キーが存在する場合に既存のレコードを更新し、存在しない場合は新しいレコードを挿入するシナリオで使おう
/icons/point.icon直接SQLを発行するのでバリデーションやコールバックをスキップする
これにより、パフォーマンスが向上しますが、データの整合性を確保するために、事前にデータの検証が必要
/icons/point.icon重複キーが存在する場合、そのレコードを更新し、存在しない場合は新しいレコードを挿入します
挿入または更新されたレコードの情報を返さない
挿入または更新に失敗したレコードの情報を直接取得する機能はない
重複キーの扱い
insert_all: 重複キーのレコードをスキップ
upsert_all: 重複キーのレコードを更新
更新の柔軟性
insert_all: 更新は行われない
upsert_all: 更新するカラムを指定できる
パフォーマンス
insert_all: 単純な挿入のため、一般的に高速
upsert_all: 更新の可能性があるため、若干のオーバーヘッドがある
ユースケース
insert_all: 新規レコードの挿入のみが必要な場合
upsert_all: レコードの挿入と更新の両方が必要な場合
メソッド引数
upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
:unique_by
一意性を判断するためのカラムまたはカラムの配列を指定
これによりどのレコードが更新されるかが決定される
例:unique_by: :id または unique_by: [:name, :email]
:on_duplicate
重複するレコードが見つかった場合の動作を指定
デフォルト値は:updateで:updateの場合、既存のレコードが更新される
update_only
更新するカラムを指定します。これにより、特定のカラムのみが更新される
例: update_only: [:name, :age]
nilの場合、すべてのカラムが更新されます
returning
操作後に戻されるカラムを指定します。特定のカラムの値を取得するために使用されます。
例: returning: [:id, :updated_at]
record_timestamps
タイムスタンプ(created_at、updated_at)を自動的に設定するかどうかを指定します。
デフォルト値はtrueです。falseに設定すると、タイムスタンプは自動的に更新されません。
発行されるSQL
code:sql
INSERT INTO users (id, name, age)
VALUES (1, 'Alice', 30), (2, 'Bob', 25), (3, 'Charlie', 35)
ON CONFLICT (id) DO UPDATE
SET name = EXCLUDED.name, age = EXCLUDED.age;
INSERT ... ON CONFLICT ... DO UPDATE構文を利用して実現している
例
code:ruby
# 例: usersテーブルに複数のレコードを一括で挿入または更新する
users = [
{ id: 1, name: 'Alice', age: 30 },
{ id: 2, name: 'Bob', age: 25 },
{ id: 3, name: 'Charlie', age: 35 }
]
User.upsert_all(users, unique_by: :id)
idが一致するレコードがすでに存在する場合、そのレコードが更新されます。存在しない場合は、新しいレコードが挿入される
code:ruby
users = [
{ id: 1, name: 'Alice', age: 30 },
{ id: 2, name: 'Bob', age: 25 },
{ id: 3, name: 'Charlie', age: 35 }
]
User.upsert_all(users, unique_by: :id, update_only: :name) 特定カラムのみ更新する
/icons/hr.icon