Problem
Hard deletes are irreversible, break audit trails and can orphan references from other records. Users make mistakes and regulators may require retention. At the same time, deleted data must disappear from normal views.
Also known as: logical-delete, tombstone-record
Mark records as deleted instead of physically removing them so they can be restored, audited or purged later.
Hard deletes are irreversible, break audit trails and can orphan references from other records. Users make mistakes and regulators may require retention. At the same time, deleted data must disappear from normal views.
Soft delete replaces DELETE with an UPDATE that sets a timestamp. The row stays in the table but is hidden from normal reads, which makes undo and audit simple.
Schema and queries.
ALTER TABLE users ADD COLUMN deleted_at timestamptz;
-- uniqueness only among live rows
CREATE UNIQUE INDEX uniq_live_email ON users (email) WHERE deleted_at IS NULL;
-- "delete"
UPDATE users SET deleted_at = now() WHERE id = 42;
-- default view
CREATE VIEW live_users AS SELECT * FROM users WHERE deleted_at IS NULL;
A nightly job runs a real DELETE for rows older than the retention window.
Alternatives exist. An archive table receives deleted rows through a trigger or application code, keeping the main table lean and its indexes small. Event-sourced systems record a deleted event instead. Temporal tables retain row history automatically. Soft delete is the simplest, but at high churn the archive table approach scales better, because dead rows no longer bloat the hot table and its indexes, and autovacuum or compaction has less to do.
Low - extra rows and index entries
Low - purge job and monitoring
Medium - everyone must respect the filter
A query forgets the filter and shows deleted data to users
Unique constraint blocks re-creating a record with the same email
Purge never runs and personal data is retained beyond policy