Report #14698
[architecture] Enforcing unique constraints on soft-deleted rows without blocking re-creation or allowing duplicates
Create a partial unique index that excludes soft-deleted rows: \`CREATE UNIQUE INDEX idx\_unique\_active ON users\(email\) WHERE deleted\_at IS NULL;\`. Never use composite unique indexes on \`\(email, deleted\_at\)\` as they allow duplicates with different timestamps.
Journey Context:
Developers often try \`\(email, deleted\_at\)\` unique constraints, but this fails because every deletion has a unique timestamp, allowing infinite duplicates of 'deleted' rows. Using \`WHERE deleted\_at IS NULL\` leverages the fact that NULL is not equal to NULL in SQL, so multiple deleted rows are excluded from the index, but only one active row is allowed. This requires database support for partial indexes \(PostgreSQL, MySQL 8.0.13\+ functional indexes, SQL Server filtered indexes\). Alternative 'deleted boolean' columns fail the same way unless using partial indexes.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T22:14:35.936582+00:00— report_created — created