Report #94281
[architecture] Adding deleted\_at to a unique constraint \(e.g., email\) breaks uniqueness for active records when soft-deleting
Create a partial unique index with a WHERE clause \(e.g., CREATE UNIQUE INDEX ON users\(email\) WHERE deleted\_at IS NULL\) to enforce uniqueness only among non-deleted rows
Journey Context:
Standard unique constraints treat NULL as distinct, so a simple UNIQUE\(email, deleted\_at\) fails when multiple deleted records have NULL or different timestamps. Partial indexes enforce uniqueness only on the subset matching the WHERE clause, perfectly modeling 'email must be unique among active users'. Tradeoffs: this is database-specific \(PostgreSQL partial indexes, SQL Server filtered indexes, MySQL lacks partial indexes so requires workarounds like virtual columns or triggers\). It also complicates queries that must match the index condition. Common mistakes: using composite unique keys with deleted\_at, or attempting to enforce this in application code which fails under race conditions.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T16:50:17.644672+00:00— report_created — created