Soft Delete Pattern
Also Known As
soft delete MySQL
deleted_at pattern
logical delete
paranoid delete
TL;DR
Marking records as deleted with a deleted_at timestamp instead of physically removing them — preserving data for auditing and recovery.
Explanation
Soft delete adds a nullable DATETIME column (commonly deleted_at or is_deleted). All queries filter WHERE deleted_at IS NULL to exclude deleted records. Advantages: data recovery, audit trail, referential integrity preservation. Disadvantages: all queries need the filter, indexes are less efficient as soft-deleted rows accumulate, unique constraints must account for soft-deleted duplicates. Hard deleting after a retention period (archiving) is common.
Common Misconception
✗ Soft delete solves the data recovery problem completely. Without a cleanup/archive strategy, soft-deleted rows accumulate indefinitely, degrading query performance and bloating the table.
Why It Matters
Physical DELETE is irreversible. Soft delete gives you recovery capability and audit history — critical for user data, financial records, and compliance.
Common Mistakes
- Forgetting the WHERE deleted_at IS NULL filter in some queries — returning deleted records.
- Unique constraints broken by soft-deleted duplicates — a deleted email re-registered violates UNIQUE.
- Not archiving old soft-deleted rows — table size grows indefinitely.
Avoid When
- Avoid soft delete for high-volume append-only data (logs, events) — deleted rows accumulate and degrade performance.
- Do not use soft delete without a cleanup/archive strategy — tables grow indefinitely.
When To Use
- Use soft delete for user data, financial records, and any content requiring audit trails or recovery.
- Use soft delete when related records reference the row via foreign keys — hard delete would violate referential integrity.
Code Examples
✗ Vulnerable
-- Hard delete — irreversible, breaks foreign keys, loses history
DELETE FROM users WHERE id = ?;
✓ Fixed
-- Schema
ALTER TABLE users ADD COLUMN deleted_at DATETIME NULL DEFAULT NULL;
CREATE INDEX idx_users_deleted ON users (deleted_at);
-- Soft delete
UPDATE users SET deleted_at = NOW() WHERE id = ?;
-- All queries filter deleted
SELECT * FROM users WHERE deleted_at IS NULL;
// PHP: unique email workaround for soft-deleted
$stmt = $pdo->prepare(
'SELECT id FROM users WHERE email = ? AND deleted_at IS NULL'
);
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
31 Mar 2026
Views
28
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 0
No pings yet today
ChatGPT 11
Perplexity 5
Google 5
Unknown AI 3
Meta AI 1
Ahrefs 1
Also referenced
How they use it
crawler 25
pre-tracking 1
Related categories
⚡
DEV INTEL
Tools & Severity
⚙ Fix effort: Low
⚡ Quick Fix
Add deleted_at DATETIME NULL DEFAULT NULL to tables and filter WHERE deleted_at IS NULL in all SELECT/UPDATE queries
📦 Applies To
web
cli
🔗 Prerequisites
🔍 Detection Hints
DELETE FROM users WHERE id = ? without soft delete consideration
Auto-detectable:
✗ No
⚠ Related Problems
🤖 AI Agent
Confidence: Low
False Positives: High
✗ Manual fix
Fix: High
Context: File
Tests: Regenerate