Материалы / Истории / Безопасный DELETE: последний взгляд
История

Безопасный DELETE: последний взгляд

История о предварительном SELECT, резервной копии и проверке выборки перед необратимым удалением данных.

Стажер Коля сидел перед монитором, как сапёр перед бомбой. Задача была простой: удалить тестовые записи, созданные до 1 сентября. В его редакторе горели два слова, от которых у любого инженера холодеет в груди: DELETE FROM.

Он перепроверил условие WHERE трижды. created_at < '2025-09-01' и status = 'test'. Логично. Безопасно. Но палец замер над клавишей Enter. А что, если?.. Что, если в полночь системное время сбоило? Или какой-то старый импорт пометил реальные заказы как тестовые?

В этот момент рядом бесшумно возник Макс с кружкой кофе. Он не заглядывал в монитор. Он посмотрел на Колю.

— Ты сейчас похож на человека, который собирается произнести необратимую фразу, — тихо сказал Макс. — Фразу, после которой мир уже не будет прежним.

Коля нервно усмехнулся:

— Да просто старые данные чищу. Но чувство именно такое.

Макс кивнул, и в его взгляде не было ни капли осуждения. Только полное, тотальное понимание.

— Все мы держим в руках скальпель. Каждый день. И грань между хирургом и мясником — не в умении резать. Она — в искусстве последнего взгляда.

Он мягко подвинул к себе клавиатуру.

— Мы не пишем команды. Мы ведём диалог с системой, в которой живут судьбы, деньги и чужие надежды. И прежде чем отдать приказ, мудрый правитель всегда спрашивает: «Покажите мне тех, кого коснётся моё решение».

Макс не стал ничего исправлять. Он скопировал условие удаления в SELECT, добавил сортировку и ограничение для ручного просмотра:

SELECT *
FROM orders
WHERE created_at < date '2025-09-01'
AND status = 'test'
ORDER BY created_at, id
LIMIT 500;

— Посмотри им в глаза, — сказал он. — Не как строкам. А как историям, которые ты собираешься стереть.

На экране появились сотни записей. Коля пробежался по ним взглядом… и замер. Одна из записей. Статус — ‘test’. Дата — августовская. Но сумма — огромная. И комментарий: «Демо для ключевого клиента N, не трогать до согласования». Эту запись завели вручную для презентации, и она попала под все формальные критерии удаления.

Он молча добавил в WHERE ещё одно условие: AND source IS DISTINCT FROM 'manual_demo'. В отличие от !=, такая проверка не потеряет строки с NULL незаметно. Затем снова запустил SELECT и отдельно проверил количество строк.

Перед удалением Коля сохранил точную выборку в резервную таблицу с номером заявки и выполнил изменение в явной транзакции:

BEGIN;
CREATE TABLE support.orders_cleanup_20250905 AS
SELECT *
FROM orders
WHERE created_at < date '2025-09-01'
AND status = 'test'
AND source IS DISTINCT FROM 'manual_demo';
DELETE FROM orders
WHERE created_at < date '2025-09-01'
AND status = 'test'
AND source IS DISTINCT FROM 'manual_demo'
RETURNING id;
-- После сверки количества и контрольной выборки:
COMMIT;
-- При любом сомнении вместо COMMIT: ROLLBACK;

Только после сверки Коля зафиксировал транзакцию. Для большой выборки этот сценарий пришлось бы разбить на пакеты: долгий DELETE сам способен создать нагрузку, удерживать старые версии строк и раздувать WAL.

Макс сделал глоток кофе.

— Видишь? — он улыбнулся. — Ты не просто удалил мусор. Ты спас ценность. Это и есть работа хирурга. Один лишний взгляд — одна спасённая вселенная.

Он похлопал его по плечу и пошёл дальше, оставив его наедине с простой, но жизненно важной истиной: самый мощный инструмент инженера — это не право менять, а мудрость не навредить.