- Hard delete:
DELETEthật — dữ liệu biến mất, bảng/index gọn, nhưng không khôi phục được và mất dấu vết. - Soft delete: chỉ đánh dấu đã xóa (cột
deleted_attimestamp,NULL= còn sống); mọi query đọc phải lọcWHERE deleted_at IS NULL. Khôi phục = set lạiNULL.
Trade-off của soft delete:
- Được: khôi phục khi thao tác nhầm, giữ lịch sử phục vụ audit, bảng con tham chiếu không gãy FK.
- Mất: mọi query phải nhớ lọc (quên một chỗ là dữ liệu "đã xóa" lộ ra UI); unique constraint hỏng — email đã "xóa" vẫn chặn đăng ký mới; bảng và index phình theo thời gian; và không thỏa yêu cầu xóa thật theo luật bảo vệ dữ liệu (GDPR right to erasure).
ALTER TABLE users ADD COLUMN deleted_at timestamptz;
-- unique email enforced only among live rows
CREATE UNIQUE INDEX users_email_live ON users(email) WHERE deleted_at IS NULL;Cách chọn thực dụng:
- Dữ liệu nghiệp vụ user có thể xóa nhầm (đơn hàng, bài viết) → soft delete + job purge xóa thật sau N ngày (grace period).
- Dữ liệu cá nhân phải xóa theo luật, bảng log/dữ liệu tạm → hard delete (hoặc anonymize).
- Cần audit đầy đủ → hướng thứ ba: copy bản ghi sang bảng archive/audit log rồi hard delete — bảng chính sạch, lịch sử vẫn còn.
Bẫy hay gặp: rải điều kiện deleted_at IS NULL bằng tay khắp code thay vì gom về một tầng chung (default scope của ORM, view); soft delete bảng cha nhưng query bảng con vẫn coi bản ghi cha là sống.