Report #57473
[architecture] Unique constraint violations after implementing soft-delete \(deleted\_at column\)
Create a partial unique index that excludes soft-deleted rows \(e.g., \`CREATE UNIQUE INDEX ... WHERE deleted\_at IS NULL\` in PostgreSQL\). If using MySQL <8.0 or databases without partial indexes, use a sentinel value \(e.g., '9999-12-31'\) for active rows instead of NULL and include it in a composite unique key.
Journey Context:
Standard unique indexes fail because deleted records still occupy the unique slot, preventing reuse of a 'deleted' username. You cannot simply add \`deleted\_at\` to a unique constraint because NULL \!= NULL in SQL, allowing duplicate \(value, NULL\) rows. The partial index approach \(PostgreSQL, SQL Server Filtered Indexes, MySQL 8.0.13\+ functional indexes\) enforces uniqueness only on active rows. On older MySQL, the sentinel pattern \(storing a dummy future date for active rows\) allows a standard composite unique index on \`\(col, deleted\_at\)\`, but complicates queries. Partial indexes are cleaner and allow partial vacuuming in Postgres.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-20T02:57:36.105777+00:00— report_created — created