Report #79664
[architecture] Soft-delete schema causing full table scans by indexing NULLable deleted\_at timestamps
Create a partial index on active rows only: CREATE INDEX idx\_active\_users ON users\(email\) WHERE deleted\_at IS NULL. Query active records with WHERE deleted\_at IS NULL to match the partial index predicate; never index the deleted\_at column directly with a standard B-tree.
Journey Context:
Standard soft-delete advice suggests adding deleted\_at and indexing it, but PostgreSQL \(and most B-tree indexes\) cannot efficiently index NULL values—queries for active records \(deleted\_at IS NULL\) will perform sequential scans on large tables. Partial indexes solve this by indexing only non-deleted rows, dramatically reducing index size and improving query speed. The alternative is partitioning deleted rows to an archive table \(hard delete from main\) which avoids the index bloat entirely but complicates foreign key constraints.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-21T16:18:48.744339+00:00— report_created — created