activerecord-import でバルクインサートした際にデフォルトだとテーブルに設定したユニークキー制約に引っかからない事象が起きたので、その原因と解決策を書きます。
バージョン
- activerecord-import: 0.16.2
- activerecord: 5.0.1
- MySQL: 5.7.12
tl;dr
-
.import
メソッドを使う時にon_duplicate_key_update: false
する
起きた事象
ユニークキー制約をかけたテーブルのモデル Hoge
に対してバルクインサートの処理を書いていました。
コードだと以下のようなものです。
hoges = params[:hoges].map do |hoge_params|
Hoge.new(inspect_create_hoge_params(hoge_params))
end
Hoge.import(hoges)
制約に引っかかっているデータが送られてきた時は例外が発生することを期待していたのですが、実際やってみると最初の1件だけ保存されるという動きをしていました。
原因
発行されているSQLのログを見ると、
INSERT INTO `hoges`
(~~中略~~)
ON DUPLICATE KEY UPDATE `hoges`.`updated_at`=VALUES(`updated_at`)
ON DUPLICATE KEY UPDATE
が怪しいなと思いリファレンスを調べてみると、
If you specify ON DUPLICATE KEY UPDATE, and a row is inserted that would cause a duplicate value in a UNIQUE index or PRIMARY KEY, MySQL performs an UPDATE of the old row.
ユニークキー制約にかかったレコードを保存する時に古いレコードをupdateする、というまさに今起こっていた動きが書いてありました。
解決策
コードを追いかけてみた結果、 on_duplicate_key_update
をfalseにしたら ON DUPLICATE KEY UPDATE
をしないように見えたので、
Hoge.import(hoges, on_duplicate_key_update: false)
としてみたところ、期待した例外が発生するようになりました。SQLの方も
INSERT INTO `hoges`
(~~中略~~)
と ON DUPLICATE KEY UPDATE
が消えていることも確認できました。