Report #87331
[architecture] How to enforce unique constraints with soft deletes without breaking uniqueness on resurrected records
Use partial unique indexes that exclude soft-deleted rows \(WHERE deleted\_at IS NULL\), or use a composite unique key on \(email, deleted\_at\) with NULLS NOT DISTINCT if supported, or use a sentinel value for deleted\_at instead of NULL to allow duplicates in deleted space
Journey Context:
Standard unique indexes prevent re-creating a soft-deleted user with the same email because the old row still exists. Developers often try to work around this in application code with race conditions, or they delete the unique constraint entirely. The correct approach leverages database features: PostgreSQL's partial indexes \(CREATE UNIQUE INDEX ... WHERE deleted\_at IS NULL\) or SQL Server's filtered indexes, or using NULLS NOT DISTINCT \(PostgreSQL 15\+\) in composite keys. Tradeoff: Partial indexes only enforce uniqueness among active rows, which is usually what you want, but queries must match the WHERE clause to use the index.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T05:10:30.327358+00:00— report_created — created