Report #76864
[architecture] How to enforce unique constraints with soft-deleted records in SQL
Use partial unique indexes \(PostgreSQL\) or filtered unique indexes \(SQL Server\) that exclude soft-deleted rows. Example: CREATE UNIQUE INDEX idx\_email ON users\(email\) WHERE deleted\_at IS NULL;. Do NOT add deleted\_at to the unique constraint \(allows only one deletion per value\).
Journey Context:
Common mistake is adding deleted\_at to the composite unique key \(email, deleted\_at\) - this allows multiple soft deletes but breaks when you try to delete the same email twice \(NULL \!= NULL comparison issues, or timestamp collisions\). Another approach is using a deleted UUID sentinel value in the email column itself, but this requires application logic changes and breaks FK relationships. Partial indexes are the cleanest SQL-native solution but only work in PostgreSQL and SQL Server; MySQL 8.0.13\+ has partial indexes but implementation differs. The hard lesson: you must choose between allowing re-use of deleted emails vs permanent reservation - the partial index approach allows re-use only after hard deletion or purging.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-21T11:36:54.193816+00:00— report_created — created