修复不存在唯一约束违规时的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插入
  • 从备份恢复

始终在批量数据操作后重置序列,以避免这些神秘的错误。