Как удалить строку в SQL

Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.

Зачем это уметь и на что обращать внимание

DELETE — одна из самых опасных операций в SQL. Запустили DELETE FROM users без WHERE — и через секунду ваша таблица пуста. Если это prod и нет свежего backup — это часы downtime и потерянные данные. Аналитик обычно делает DELETE реже, чем SELECT, но когда делает — ошибка здесь выходит дороже всего.

Поэтому важно знать не только синтаксис, но и паттерны безопасного удаления: транзакция с ROLLBACK, проверка SELECT COUNT перед DELETE, обработка FK-связей, разница между DELETE и TRUNCATE, альтернатива через soft-delete (помечать как deleted вместо физического удаления).

В статье — практические рецепты:

  • DELETE с WHERE и подзапросом
  • DELETE с JOIN в Postgres и MySQL (синтаксис разный)
  • TRUNCATE vs DELETE (когда что)
  • LIMIT в DELETE для пакетного удаления больших таблиц
  • Soft delete — современный стандарт для prod
  • CASCADE delete и как не уронить пол-базы
  • Чек-лист безопасного удаления

Базовый синтаксис

DELETE FROM users WHERE id = 42;

Внимание: без WHERE удалит все строки:

DELETE FROM users;  -- удаляет ВСЕ!

1. Удаление по условию

-- одного
DELETE FROM orders WHERE order_id = 100;

-- по условию
DELETE FROM users WHERE last_login < NOW() - INTERVAL '2 years';

-- по списку
DELETE FROM users WHERE id IN (1, 5, 10);

2. С JOIN (другой таблицы)

Postgres

DELETE FROM orders o
USING users u
WHERE o.user_id = u.id AND u.is_deleted = TRUE;

MySQL

DELETE o FROM orders o
INNER JOIN users u ON o.user_id = u.id
WHERE u.is_deleted = TRUE;

3. С подзапросом

DELETE FROM orders
WHERE user_id IN (
    SELECT id FROM users WHERE is_active = FALSE
);

4. TRUNCATE — удалить всё быстро

TRUNCATE TABLE users;

Разница с DELETE FROM users:

DELETE TRUNCATE
Скорость медленнее быстрее (reset + drop pages)
Триггеры срабатывают да нет
Откат в транзакции да да (большинство СУБД)
Auto-increment сохраняется сбрасывается
FK проверяются частично блокируется

5. Безопасное удаление (проверка перед)

-- 1. SELECT — сколько удалим
SELECT COUNT(*) FROM orders WHERE created_at < '2023-01-01';

-- 2. Транзакция
BEGIN;
DELETE FROM orders WHERE created_at < '2023-01-01';

-- 3. Проверка
SELECT COUNT(*) FROM orders;

-- 4. Commit или rollback
COMMIT;
-- или ROLLBACK;

6. LIMIT на DELETE

Для постепенного удаления (большие таблицы):

MySQL

DELETE FROM orders WHERE created_at < '2023-01-01' LIMIT 1000;

Повторять в цикле.

Postgres (без LIMIT в DELETE)

DELETE FROM orders
WHERE order_id IN (
    SELECT order_id FROM orders
    WHERE created_at < '2023-01-01'
    LIMIT 1000
);

7. Soft delete (рекомендуется)

Вместо DELETE — помечать как удалённое:

UPDATE users
SET deleted_at = NOW()
WHERE id = 42;

-- потом выборки с фильтром
SELECT * FROM users WHERE deleted_at IS NULL;

Плюсы:

  • Можно восстановить
  • Аудит истории
  • Сохранение FK-целостности

Минусы:

  • Данные остаются (не для GDPR)

8. CASCADE delete

Если FK с ON DELETE CASCADE:

-- при удалении user автоматически удалятся его orders
DELETE FROM users WHERE id = 42;

Осторожно — может удалить много больше, чем вы думали.

9. RETURNING — получить удалённое

Postgres:

DELETE FROM orders
WHERE created_at < '2023-01-01'
RETURNING order_id, total;

Возвращает удалённые строки — полезно для аудита.

10. Откат удаления

В транзакции

BEGIN;
DELETE FROM users WHERE id = 42;
-- передумал
ROLLBACK;
-- данные вернулись

После COMMIT

  • Postgres / MySQL: restore из backup / WAL / binlog
  • Сложно без заранее настроенной точки восстановления

Поэтому всегда делайте BACKUP перед массовым DELETE:

CREATE TABLE users_backup AS SELECT * FROM users;

Безопасный порядок работы

  1. Backup таблицы или dump
  2. SELECT COUNT — сколько удалите
  3. Транзакция BEGIN
  4. DELETE с WHERE
  5. Проверка (COUNT, примеры строк)
  6. COMMIT (или ROLLBACK)

Частые ошибки

1. DELETE без WHERE

Классика. Удаляет всю таблицу. Всегда проверяйте WHERE.

2. DELETE вместо UPDATE

«Сначала удалю, потом вставлю» → лучше UPDATE. Меньше рисков и быстрее.

3. Не учесть FK

Если на таблицу ссылаются другие таблицы с FK — нужно сначала удалить оттуда, или использовать CASCADE.

4. Массовый DELETE без транзакции

На большой таблице может блокировать часами. Удаляйте батчами в транзакциях.

5. TRUNCATE вместо DELETE

TRUNCATE нельзя отменить в некоторых СУБД. Используйте только для явного «полного сброса».

Связанные темы

FAQ

DELETE или soft delete?

Soft delete безопаснее и аудитируется. DELETE — для GDPR / полного удаления.

TRUNCATE быстрее?

Да, в 10-100 раз для больших таблиц. Но не ставит row-level locks, осторожно с FK.

Можно ли отменить DELETE?

В транзакции — да (ROLLBACK). После COMMIT — только из backup.

DELETE на FK — что будет?

ON DELETE RESTRICT (default) — ошибка. CASCADE — удалит зависимые.


Тренируйте SQL — откройте тренажёр с 1500+ вопросами для собесов.