Перейти к содержанию
Innovialabs

SQL INSERT, UPDATE и DELETE: как безопасно изменять данные

Основы4 мин

Коротко

INSERT добавляет строки, UPDATE меняет их, DELETE удаляет; чтобы изменять данные безопасно, проверьте условие WHERE через SELECT, выполните изменение в транзакции и сверьте число затронутых строк до COMMIT.

Коротко

Данные в SQL меняют три команды:

  • INSERT добавляет новые строки.
  • UPDATE меняет значения в существующих строках.
  • DELETE удаляет строки.

UPDATE и DELETE затрагивают все строки, подходящие под WHERE. Если условие ошибочное или его нет, команда всё равно выполнится — по чужим строкам или по всей таблице. Безопасность здесь — это прежде всего проверка WHERE до того, как изменение станет окончательным.

INSERT: добавление строк

INSERT INTO customers (name, email, city)
VALUES ('Aziza Karimova', 'aziza@example.com', 'Tashkent');

-- несколько строк сразу
INSERT INTO customers (name, email, city)
VALUES ('Bekzod', 'bek@example.com', 'Samarkand'),
       ('Dilnoza', 'dil@example.com', 'Bukhara');

-- вставка результата запроса
INSERT INTO archived_orders (id, total, created_at)
SELECT id, total, created_at FROM orders WHERE created_at < '2024-01-01';

Всегда перечисляйте колонки явно. Без списка колонок вставка зависит от их порядка и ломается или тихо пишет значения не туда, когда кто-то меняет таблицу.

UPDATE: изменение строк

UPDATE products
SET price = price * 1.1, updated_at = now()
WHERE category_id = 7;

В SET можно использовать текущее значение колонки, другие колонки и выражения. Одна команда может изменить тысячи строк, поэтому главное в ней — WHERE.

DELETE: удаление строк

DELETE FROM sessions
WHERE expires_at < now();

Чтобы очистить таблицу целиком, обычно быстрее TRUNCATE, но он обходит построчную логику, и отменить его сложнее. Для бизнес-таблиц часто надёжнее мягкое удаление (колонка deleted_at): данные можно восстановить, история не теряется.

Привычки, которые спасают от аварий

1. Сначала SELECT с тем же WHERE

Перед UPDATE или DELETE выполните SELECT с точно таким же условием:

SELECT count(*) FROM orders WHERE status = 'draft' AND created_at < '2025-01-01';

Ждали 40 строк, а видите 40 000 — остановитесь. Затем перенесите WHERE в изменение без правок.

2. Оборачивайте изменение в транзакцию

BEGIN;
UPDATE orders SET status = 'cancelled'
WHERE status = 'draft' AND created_at < '2025-01-01';
-- клиент покажет, сколько строк затронуто
SELECT status, count(*) FROM orders GROUP BY status;
COMMIT;   -- или ROLLBACK, если что-то не так

Внутри транзакции результат можно проверить и откатить. Учтите: некоторые инструменты (и MySQL с включённым autocommit) фиксируют каждую команду сразу, если транзакцию не начать явно. Не держите такие транзакции открытыми долго — они удерживают блокировки.

3. RETURNING показывает, что изменилось

PostgreSQL (и ряд других СУБД) умеет возвращать затронутые строки:

DELETE FROM coupons
WHERE expires_at < now()
RETURNING id, code;

Это дешёвый журнал изменений, а после INSERT — способ получить сгенерированные ID без отдельного SELECT.

4. UPSERT вместо «проверить, потом вставить»

Если строка может уже существовать, схема «сначала проверить, потом вставить» даёт гонки. Используйте одну атомарную команду. Для неё нужен уникальный ключ.

-- PostgreSQL
INSERT INTO stock (sku, qty) VALUES ('A-100', 5)
ON CONFLICT (sku) DO UPDATE SET qty = stock.qty + EXCLUDED.qty;

-- MySQL
INSERT INTO stock (sku, qty) VALUES ('A-100', 5)
ON DUPLICATE KEY UPDATE qty = qty + VALUES(qty);

В новых версиях MySQL вместо VALUES() в этом месте рекомендуют алиас строки — сверьтесь с документацией своей версии.

Классическая авария: UPDATE без WHERE

UPDATE users SET password_hash = '...';   -- у всех пользователей, а не у одного

Обычно так бывает, когда запускают выделенный кусок запроса, теряют последнюю строку или правят запрос в спешке. Защита:

  • Пишите сначала WHERE, потом SET.
  • В MySQL включите safe updates (--safe-updates): он отклоняет UPDATE и DELETE без условия по ключу или LIMIT.
  • Не давайте учётным записям приложения и аналитиков лишних прав; изменения на проде — через проверенные скрипты и миграции.
  • Держите проверенные бэкапы и восстановление на момент времени — после COMMIT транзакция уже не поможет.

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

  • Правки на проде прямо из GUI-клиента с включённым autocommit.
  • Одна огромная команда, которая на минуты блокирует большую таблицу; лучше делить на пачки.
  • Сборка SQL склейкой строк вместо параметров — прямая дорога к SQL-инъекциям.
  • Удаление родительских строк без понимания внешних ключей и ON DELETE CASCADE.

Частые вопросы

Средствами самого SQL — нет. Нужен бэкап, восстановление на момент времени или схема с мягким удалением. Поэтому проверять результат стоит до COMMIT.

Поможем с этим

Если хотите не разбираться самим, а получить результат — этим мы занимаемся каждый день.

Обсудить проект

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.