一意制約違反がないのにPG::UniqueViolationを修正する方法

重複データがないことが確実なのにPG::UniqueViolationエラーに遭遇したことはありませんか?原因は多くの場合、同期が外れたPostgreSQLシーケンスです。

問題

次のようなエラーが表示されます:

PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "users_pkey"
DETAIL: Key (id)=(42) already exists.

しかし確認すると、ID 42のレコードは存在しません。何が起きているのでしょうか?

原因

PostgreSQLはシーケンスを使用して自動増分IDを生成します。時々、特にデータのインポートや手動での挿入の後、シーケンスがテーブル内の実際の最大IDと同期が外れることがあります。

修正方法

シーケンスを正しい値にリセットします:

SELECT setval('users_id_seq', (SELECT MAX(id) FROM users));

またはRailsで:

ActiveRecord::Base.connection.execute(
  "SELECT setval('users_id_seq', (SELECT MAX(id) FROM users))"
)

すべてのテーブルに対して

すべてのシーケンスを一度に修正するには:

ActiveRecord::Base.connection.tables.each do |table|
  ActiveRecord::Base.connection.reset_pk_sequence!(table)
end

予防

この問題は一般的に以下の後に発生します:

  • データの移行またはインポート
  • 明示的なIDを使用した手動SQLの挿入
  • バックアップからの復元

これらの不思議なエラーを避けるために、大量データ操作の後は常にシーケンスをリセットしてください。