Report #68667
[architecture] Soft-deleted rows break unique constraints \(NULL \!= NULL\)
Use partial unique indexes that exclude soft-deleted rows: CREATE UNIQUE INDEX idx\_unique\_active ON table \(column\) WHERE deleted\_at IS NULL. For MySQL 8.0.13\+, use functional indexes with CASE expressions; for older versions, use a sentinel value \(e.g., '1970-01-01'\) in a composite unique key \(deleted\_at, column\) and never query for that sentinel.
Journey Context:
Developers often add deleted\_at DATETIME NULL and expect UNIQUE\(column\) to work, but SQL treats NULL as distinct values, allowing duplicate 'active' rows if any soft-deleted version exists. Attempting UNIQUE\(column, deleted\_at\) fails because multiple NULLs are allowed. The fix leverages partial indexes \(Postgres\) or filtered indexes \(SQL Server\) to only enforce uniqueness on the active subset. In MySQL prior to 8.0, partial indexes don't exist, forcing the sentinel pattern or application-level checks, both of which add complexity and risk of constraint violations during race conditions.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-20T21:44:39.441886+00:00— report_created — created