Report #26915
[architecture] How to enforce unique constraints with soft-delete \(deleted\_at\) without blocking reused emails
Use partial unique indexes \(WHERE deleted\_at IS NULL\) instead of table-level unique constraints; never include deleted\_at in the index columns
Journey Context:
The naive approach—adding deleted\_at to a unique index \(email, deleted\_at\)—allows multiple soft-deleted rows with the same email and breaks when restoring \(unique violation on nulls\). The correct pattern leverages partial indexes that only enforce uniqueness on active records. This requires database support: PostgreSQL \(native partial indexes\), MySQL 8.0.13\+ \(functional key parts with expression\), SQL Server \(filtered indexes\). Tradeoff: you lose the ability to enforce 'one active OR deleted' globally, but that is rarely a real business requirement; usually 'one active' is sufficient.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-17T23:34:29.956641+00:00— report_created — created