Anonymising rows that must be kept and must not identify

Financial records retained for seven years, containing personal data that may not be retained that long.

UPDATE orders
SET customer_name  = CONCAT('redacted-', id),
    customer_email = CONCAT('redacted-', id, '@invalid'),
    billing_line_1 = NULL,
    billing_postcode = LEFT(billing_postcode, 3)
WHERE placed_at < DATE_SUB(NOW(), INTERVAL 2 YEAR)
  AND customer_name NOT LIKE 'redacted-%'
LIMIT 5000;

Keeping the postcode district and discarding the rest is the compromise that preserves the regional analysis the finance team actually uses, which is the sort of requirement that only surfaces when you propose deleting a column. The NOT LIKE makes the batch idempotent, which matters because it runs nightly and will be interrupted.