UPDATE: изменение данных
UPDATE: изменение данных
INSERT добавляет новые строки. UPDATE изменяет существующие. Это самый рискованный оператор DML для новичков: если забыть WHERE, изменятся все строки в таблице. В этом уроке разберём синтаксис, типичные случаи применения и главное правило безопасности.
Синтаксис UPDATE
UPDATE таблица
SET col1 = value1,
col2 = value2
WHERE условие;
Пример — обновить email одного клиента:
UPDATE customers
SET email = 'new_anna@example.com'
WHERE id = 5;
СУБД найдёт строки, удовлетворяющие WHERE id = 5, и обновит колонку email. Если условие совпадает с несколькими строками — обновятся все.
Главное правило: всегда WHERE
-- ОПАСНО: обновит все строки в таблице!
UPDATE customers
SET city = 'Москва';
-- Безопасно: обновит только конкретного клиента
UPDATE customers
SET city = 'Москва'
WHERE id = 42;
Первый запрос присвоит city = 'Москва' всем клиентам в базе. Это необратимое действие, если нет резервной копии. PostgreSQL не предупреждает и не спрашивает подтверждения.
Правило безопасности: перед UPDATE всегда выполните соответствующий SELECT с тем же WHERE и убедитесь, что он возвращает именно те строки, которые нужно изменить:
-- Сначала проверьте
SELECT * FROM customers WHERE id = 42;
-- Убедились что всё правильно — теперь обновляем
UPDATE customers SET city = 'Москва' WHERE id = 42;
Обновление нескольких колонок
SET принимает несколько присвоений через запятую:
UPDATE customers
SET city = 'Санкт-Петербург',
email = 'updated@example.com',
phone = '+7-921-123-4567'
WHERE id = 5;
Все три колонки обновляются атомарно — либо все, либо ни одна (в рамках транзакции).
Вычисляемые значения в SET
Правая часть присвоения — выражение, которое может ссылаться на текущее значение той же колонки:
-- Повышение зарплаты на 10%
UPDATE employees
SET salary = salary * 1.10
WHERE department = 'IT';
-- Добавить бонус к текущей сумме
UPDATE orders
SET total = total + 500
WHERE status = 'completed' AND created_at >= '2024-01-01';
salary = salary * 1.10 — читает текущее значение salary, умножает на 1.1 и записывает обратно. Это атомарно: не нужно читать значение отдельным SELECT.
RETURNING в UPDATE
Как и INSERT, UPDATE поддерживает RETURNING:
UPDATE employees
SET salary = salary * 1.10
WHERE department = 'IT'
RETURNING id, name, salary;
Возвращает новые значения после обновления. Это полезно, когда нужно немедленно использовать обновлённые данные в приложении.
Проверь себя: вернёт ли RETURNING старые или новые значения колонки salary?
UPDATE с подзапросом
Значение в SET может быть подзапросом:
-- Установить зарплату каждому сотруднику = средняя по его отделу
UPDATE employees e
SET salary = (
SELECT AVG(salary)
FROM employees e2
WHERE e2.department = e.department
);
Коррелированный подзапрос вычисляет среднюю зарплату по отделу для каждого сотрудника. Это пример, который скорее для демонстрации — в реальности так уравнивать зарплаты не стоит.
UPDATE ... FROM: обновление с данными из другой таблицы
PostgreSQL позволяет UPDATE с FROM — обновлять строки на основе данных из другой таблицы:
UPDATE orders o
SET status = 'shipped'
FROM shipments s
WHERE s.order_id = o.id
AND s.shipped_at IS NOT NULL
AND o.status = 'paid';
FROM shipments добавляет данные из другой таблицы в UPDATE-запрос. Это эффективнее, чем обновлять через подзапрос для каждой строки. Результат: заказы, для которых есть запись об отправке, переходят в статус shipped.
NULL в UPDATE
Присвоить NULL явно:
UPDATE customers
SET phone = NULL
WHERE id = 7;
Это работает — phone получает NULL. Нельзя сделать только если колонка объявлена NOT NULL:
UPDATE customers SET email = NULL WHERE id = 7;
-- ERROR: null value in column "email" violates not-null constraint (если email NOT NULL)
Сколько строк обновил UPDATE
PostgreSQL возвращает сообщение UPDATE N — количество обновлённых строк. Если WHERE не нашёл ни одной строки — UPDATE 0, не ошибка. Это важно: UPDATE 0 означает, что условие не совпало с данными, а не что произошла ошибка.
Приложения часто проверяют это число: если ожидали обновить 1 строку, а получили 0 — значит объект не найден.
UPDATE в транзакции: безопасное обновление
Если не уверены в правильности UPDATE, выполните его внутри транзакции:
BEGIN;
-- Смотрим что изменится
SELECT * FROM customers WHERE city = 'Казань';
-- Обновляем
UPDATE customers SET city = 'Казань (обл.)' WHERE city = 'Казань';
-- Проверяем результат
SELECT * FROM customers WHERE city = 'Казань (обл.)';
-- Если всё верно — подтверждаем
COMMIT;
-- Если что-то не так — отменяем
-- ROLLBACK;
BEGIN начинает транзакцию. COMMIT фиксирует изменения. ROLLBACK отменяет всё, что было после BEGIN. До COMMIT изменения видны только в текущем соединении — другие пользователи ещё видят старые данные. Подробнее о транзакциях — в модуле 10.
Этот паттерн — стандартная практика при любых рискованных UPDATE или DELETE.
Типичные ошибки
1. UPDATE без WHERE:
UPDATE products SET price = 0; -- обнулит цену у всех товаров!
2. Неправильное условие WHERE:
UPDATE users SET role = 'admin' WHERE name = 'Иван';
-- Может обновить нескольких пользователей с одинаковым именем
-- Лучше: WHERE id = 123
3. Обновление связанных данных (нарушение FK):
UPDATE customers SET id = 999 WHERE id = 1;
-- ERROR: если orders.customer_id = 1 и FK настроен без CASCADE
Практический пример: смена статуса заказов
-- Все заказы старше 30 дней в статусе 'paid' → 'abandoned'
UPDATE orders
SET status = 'abandoned',
updated_at = NOW()
WHERE status = 'paid'
AND created_at < NOW() - INTERVAL '30 days'
RETURNING id, customer_id, amount;
INTERVAL '30 days' — PostgreSQL-синтаксис для вычитания интервала из даты. Возвращённые строки можно использовать для отправки уведомлений клиентам.
Массовое обновление по условию
UPDATE с WHERE на диапазон значений — стандартный инструмент для пакетных операций:
-- Применить скидку 15% ко всем товарам категории 'Одежда'
UPDATE products
SET price = price * 0.85,
discounted_at = NOW()
WHERE category = 'Одежда'
AND price > 1000;
-- Деактивировать пользователей, не заходивших более года
UPDATE users
SET status = 'inactive',
deactivated_at = NOW()
WHERE status = 'active'
AND last_login_at < NOW() - INTERVAL '1 year';
Оба примера обновляют сразу множество строк. PostgreSQL обрабатывает их эффективно — один обход таблицы. Проверьте RETURNING id чтобы узнать, сколько строк изменилось.
Краткий итог
UPDATE таблица SET col = value WHERE условие— обновляет строки по условию- Без
WHEREобновятся все строки — всегда проверяйтеSELECTпередUPDATE SETпринимает несколько присвоений через запятую; значения могут ссылаться на текущиеRETURNINGвозвращает новые значения обновлённых строкUPDATE ... FROMпозволяет обновлять с данными из другой таблицыUPDATE 0строк — не ошибка, просто условие не совпало
UPDATE и конкурентный доступ
В многопользовательской системе два запроса могут пытаться обновить одну строку одновременно. PostgreSQL использует MVCC (Multi-Version Concurrency Control) для изоляции — каждая транзакция видит согласованный снимок данных. Последний COMMIT побеждает, и изменения не теряются в неожиданном порядке.
Для случаев, когда нужна гарантия эксклюзивного обновления (например, «взять задачу из очереди»), используют SELECT ... FOR UPDATE — блокировку строки на время транзакции. Это продвинутая тема, но важно знать, что такой механизм существует.
Что дальше
Вы умеете изменять данные. Следующий урок — DELETE: удаление строк. Та же логика безопасности с WHERE — плюс особенности удаления связанных данных.