INSERT, UPDATE, DELETE — изменение данных в SQL
Коротко
INSERT, UPDATE и DELETE — DML-команды (Data Manipulation Language) для изменения данных в таблицах. INSERT добавляет строки, UPDATE изменяет существующие, DELETE удаляет. Аналитику эти команды нужны для подготовки тестовых данных, заполнения витрин, исправления ошибок в данных. На собеседованиях спрашивают разницу между DELETE и TRUNCATE, безопасность UPDATE без WHERE и RETURNING.
INSERT — вставка данных
Одна строка
INSERT INTO users (username, email, created_at)
VALUES ('ivan', 'ivan@example.com', NOW());Столбцы, не указанные в списке, получат DEFAULT или NULL. Если столбец NOT NULL и без DEFAULT — ошибка.
Несколько строк
INSERT INTO products (name, price, category)
VALUES
('Ноутбук', 75000, 'electronics'),
('Наушники', 5000, 'electronics'),
('Книга SQL', 1200, 'books');Одна команда INSERT с несколькими VALUES работает быстрее, чем три отдельных INSERT.
INSERT ... SELECT
INSERT INTO monthly_revenue (month, revenue, orders)
SELECT
DATE_TRUNC('month', order_date) AS month,
SUM(amount) AS revenue,
COUNT(*) AS orders
FROM orders
WHERE order_date >= '2025-01-01'
GROUP BY DATE_TRUNC('month', order_date);Вставляет результат запроса. Удобно для наполнения витрин данных и агрегированных таблиц.
INSERT ... RETURNING (PostgreSQL)
INSERT INTO users (username, email)
VALUES ('maria', 'maria@example.com')
RETURNING user_id, created_at;RETURNING возвращает вставленные данные — не нужен отдельный SELECT для получения сгенерированного ID.
INSERT ... ON CONFLICT (UPSERT)
INSERT INTO daily_metrics (metric_date, dau, revenue)
VALUES ('2025-03-15', 1500, 250000)
ON CONFLICT (metric_date)
DO UPDATE SET
dau = EXCLUDED.dau,
revenue = EXCLUDED.revenue;Если строка с таким metric_date уже есть — обновить. Если нет — вставить. EXCLUDED ссылается на значения, которые пытались вставить. В MySQL аналог — INSERT ... ON DUPLICATE KEY UPDATE.
UPDATE — обновление данных
Базовый синтаксис
UPDATE users
SET email = 'new@example.com'
WHERE user_id = 42;Никогда не запускайте UPDATE без WHERE (если не хотите обновить все строки в таблице).
Обновление нескольких столбцов
UPDATE products
SET
price = price * 1.1,
updated_at = NOW()
WHERE category = 'electronics';Увеличивает цену на 10% для всей электроники. Можно использовать текущее значение столбца в выражении.
UPDATE с подзапросом
UPDATE users
SET segment = 'premium'
WHERE user_id IN (
SELECT user_id
FROM orders
GROUP BY user_id
HAVING SUM(amount) > 100000
);Помечает как premium пользователей с суммой заказов больше 100 000.
UPDATE с JOIN (PostgreSQL)
UPDATE orders o
SET status = 'vip_order'
FROM users u
WHERE o.user_id = u.user_id
AND u.segment = 'premium';В PostgreSQL UPDATE ... FROM позволяет обновлять на основе данных из другой таблицы. В MySQL — UPDATE orders o JOIN users u ON ... SET ....
UPDATE ... RETURNING
UPDATE products
SET is_active = FALSE
WHERE stock = 0
RETURNING product_id, name;Возвращает обновлённые строки — удобно для логирования изменений.
DELETE — удаление данных
Базовый синтаксис
DELETE FROM orders
WHERE order_date < '2020-01-01';Удалит все заказы до 2020 года. Без WHERE удалит все строки.
DELETE с подзапросом
DELETE FROM user_sessions
WHERE user_id NOT IN (
SELECT user_id FROM users
);Удалит «осиротевшие» сессии, у которых нет соответствующего пользователя.
DELETE с USING (PostgreSQL)
DELETE FROM order_items oi
USING orders o
WHERE oi.order_id = o.order_id
AND o.status = 'cancelled';Удаляет позиции отменённых заказов. Аналог DELETE с JOIN.
DELETE ... RETURNING
DELETE FROM expired_tokens
WHERE expires_at < NOW()
RETURNING token_id, user_id;Безопасность: транзакции
Перед массовыми UPDATE или DELETE — оберните в транзакцию:
BEGIN;
-- Сначала проверить, что затронет
SELECT COUNT(*) FROM users WHERE last_login < '2023-01-01';
-- Если всё ок — выполнить
DELETE FROM users WHERE last_login < '2023-01-01';
-- Проверить результат и подтвердить
-- COMMIT; -- подтвердить
-- ROLLBACK; -- отменитьПока не выполнен COMMIT, изменения не применены. ROLLBACK откатывает всё до BEGIN. Это спасёт от случайного UPDATE users SET email = 'oops' без WHERE.
DELETE vs TRUNCATE
| Критерий | DELETE | TRUNCATE |
|---|---|---|
| Фильтрация WHERE | Да | Нет (все строки) |
| Транзакция | Да | Зависит от СУБД |
| Триггеры | Срабатывают | Не срабатывают |
| Скорость | Медленнее (построчно) | Быстрее (сброс таблицы) |
| RETURNING | Да (PostgreSQL) | Нет |
| Автоинкремент | Не сбрасывает | Сбрасывает |
Подробнее — в сравнении DELETE vs TRUNCATE.
Типичные ошибки
UPDATE/DELETE без WHERE. Самая опасная ошибка. UPDATE users SET role = 'admin' сделает всех админами. Всегда добавляйте WHERE или оборачивайте в транзакцию.
INSERT с неправильным порядком столбцов. INSERT INTO users VALUES (...) без указания столбцов зависит от порядка столбцов в таблице. Всегда указывайте список столбцов явно.
Нарушение FOREIGN KEY. INSERT строки с user_id, которого нет в таблице users — ошибка внешнего ключа. DELETE пользователя, у которого есть заказы — тоже ошибка (если нет ON DELETE CASCADE).
Забыли RETURNING. После INSERT с SERIAL приходится делать отдельный SELECT для получения ID. RETURNING решает эту проблему в одном запросе (PostgreSQL, Oracle).
Вопросы с собеседований
-- Чем отличается DELETE от TRUNCATE? -- DELETE удаляет строки с возможностью WHERE, поддерживает транзакции и триггеры. TRUNCATE очищает всю таблицу быстрее, но без фильтрации. DELETE не сбрасывает автоинкремент, TRUNCATE — сбрасывает.
-- Что произойдёт при UPDATE без WHERE? -- Обновятся все строки в таблице. Это легальная операция, но почти всегда ошибка. Используйте транзакции для защиты.
-- Что такое UPSERT?
-- INSERT с обработкой конфликта: если строка существует — обновить, если нет — вставить. В PostgreSQL: INSERT ... ON CONFLICT ... DO UPDATE. В MySQL: INSERT ... ON DUPLICATE KEY UPDATE.
-- Как вставить данные из одной таблицы в другую?
-- INSERT INTO target_table (col1, col2) SELECT col1, col2 FROM source_table WHERE .... Типы и количество столбцов должны совпадать.
-- Зачем нужен RETURNING? -- Возвращает изменённые строки без дополнительного SELECT. Полезно для получения сгенерированных ID после INSERT, подтверждения обновлений и логирования удалений. Работает в PostgreSQL.
Потренируйтесь решать задачи — откройте тренажёр с 1500+ вопросами для подготовки к собеседованиям аналитиков.
FAQ
Можно ли откатить DELETE?
Да, если DELETE выполнен внутри транзакции (BEGIN ... ROLLBACK). Без транзакции — данные удалены безвозвратно. TRUNCATE в PostgreSQL тоже можно откатить внутри транзакции, но в MySQL — нельзя.
INSERT ... SELECT vs CREATE TABLE ... AS SELECT?
INSERT ... SELECT добавляет данные в существующую таблицу. CREATE TABLE ... AS SELECT создаёт новую таблицу и заполняет данными. Второй вариант быстрее для одноразовых витрин.
Как обновить миллионы строк без блокировки?
Обновляйте батчами: UPDATE ... WHERE id BETWEEN 1 AND 10000, затем следующий диапазон. Это снижает нагрузку на БД и не блокирует другие запросы надолго.
Как тренироваться
DML-команды — часть базового SQL для аналитика. В тренажёре Карьерник есть задачи на INSERT, UPDATE, DELETE и проектирование запросов. Больше вопросов — в разделе с примерами.