Report #16996
[architecture] Cannot enforce unique constraints \(e.g., email\) on soft-deleted records without allowing duplicate NULLs
Use a partial unique index \(PostgreSQL\) or filtered index \(SQL Server\) with a WHERE clause: CREATE UNIQUE INDEX idx\_email ON users\(email\) WHERE deleted\_at IS NULL. Never include deleted\_at in the unique constraint columns.
Journey Context:
The naive approach is UNIQUE\(email, deleted\_at\). This fails because SQL NULL comparison semantics mean \([email protected], NULL\) \!= \([email protected], NULL\), allowing multiple active users with the same email if deleted\_at is NULL. It also allows the same email with different non-NULL timestamps. The robust pattern uses a partial index that only indexes rows where deleted\_at IS NULL. This enforces uniqueness strictly among active records while ignoring deleted ones entirely. In MySQL 8.0.13\+, functional indexes can approximate this, but PostgreSQL's partial index is the canonical implementation.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-17T04:14:21.221454+00:00— report_created — created