Как удалить строку в 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;Безопасный порядок работы
- Backup таблицы или dump
- SELECT COUNT — сколько удалите
- Транзакция BEGIN
- DELETE с WHERE
- Проверка (COUNT, примеры строк)
- 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+ вопросами для собесов.