activerecord-import の on_duplicate_key_update, on_duplicate_key_ignore 周りを調査

背景

activerecord-importのバージョンを0.15.0から1.2.0(最新)にアップデートしたい。 on_duplicate_key_update周りでBREAKING_CHANGEがある。 DBはMySQLを使用している。

on_duplicate_key_updateとは

MySQL

https://dev.mysql.com/doc/refman/5.6/ja/insert-on-duplicate.html

UNIQUEインデックスまたはPRIMARY KEYが重複するKeyを持つ(つまりduplicate keyな)行が挿入された場合に、どのカラムを更新するか指定できる。

activerecord-import

https://github.com/zdennis/activerecord-import/#duplicate-key-update

MySQL, PostgreSQL (9.5+), and SQLite (3.24.0+) support on duplicate key update (also known as "upsert") which allows you to specify fields whose values should be updated if a primary or unique key constraint is violated.

BREAKING_CHANGE

https://github.com/zdennis/activerecord-import/blob/ee0b95c6a3f17a3d76de340e2a6f4eecc4a7de16/CHANGELOG.md#breaking-changes

Previously :on_duplicate_key_update was enabled by default for MySQL. The update timestamp columns (updated_at, updated_on) would be updated on duplicate key. This was behavior is inconsistent with the other database adapters and could also be considered surprising. Going forward it must be explicitly enabled. See #548.

今まではon_duplicate_key_updateオプションにデフォルトでupdated_atが入っていたが、アップデートでそれが無くなる。

実装

on_duplicate_key_ignoreとは

https://github.com/zdennis/activerecord-import/#duplicate-key-ignore

MySQL, SQLite, and PostgreSQL (9.5+) support on_duplicate_key_ignore which allows you to skip records if a primary or unique key constraint is violated. MySQL it uses INSERT IGNORE

duplicate keyな行が挿入された場合に、レコードの更新をしないためのオプション。

https://dev.mysql.com/doc/refman/5.6/ja/insert.html

IGNORE キーワードを使用した場合、INSERT ステートメントの実行中に発生したエラーは無視されます。たとえば、IGNORE を使用しない場合は、テーブル内の既存の UNIQUE インデックスまたは PRIMARY KEY 値を複製する行によって重複キーエラーが発生し、このステートメントは中止されます。IGNORE を指定すると、その行が破棄され、エラーは発生しません。代わりに、無視されたエラーが警告を生成する可能性がありますが、重複キーエラーは生成しません。

エラーを抑制するオプションで、エラーに気付くづらくなるというデメリットがあるため、基本的には指定しないほうが良さそう。

not null制約に関するエラーも無視するっぽい https://stackoverflow.com/a/548570

Inserting a NULL into a column with a NOT NULL constraint.

調査

duplicate keyなレコードをimportしたときにどのようにupdateされるか調査。

ver 0.15.0

on_duplicate_key_update を指定しない場合

updated_atだけ更新されている。

on_duplicate_key_update[:name] を指定した場合

nameとupdated_atが更新されている。

allオプションを指定した場合

ver 0.15.0では指定できなかった。0.26.0からは使えそう。

https://github.com/zdennis/activerecord-import/blob/ee0b95c6a3f17a3d76de340e2a6f4eecc4a7de16/CHANGELOG.md#new-features-7

ver 1.2.0

on_duplicate_key_update を指定しない場合

Duplicate entryエラーがでた。内部的にはINSERTしているだけだから当然か。

on_duplicate_key_update([:name]) を指定した場合

nameとupdated_atが更新された。 updated_atは指定しなくても更新されるっぽい。

on_duplicate_key_update([:updated_at]) を指定した場合

nameは更新されず、updated_atだけ更新される。