Обновление данных, оператор UPDATE
Добавленные данные не всегда остаются верными: люди меняют фамилии, в платежах находят опечатки, цены приходится пересчитывать. Чтобы исправить строку, не удаляя её, используют оператор UPDATE.
Общая структура запроса с оператором UPDATE
UPDATE имя_таблицы
SET поле_таблицы = значение [, ...]
[WHERE условие_выборки];- [ ]необязательная часть
- ...можно повторить
- имя_таблицы
- Таблица, в которой меняются записи.
- поле_таблицы
Столбец, которому присваивается новое значение. Пар поле_таблицы = значение может быть несколько, через запятую.
- значение
Новое значение: число, строка, выражение вроде price * 1.1 или NULL.
- условие_выборки
Какие строки изменить. Без WHERE изменятся все строки таблицы.
Необязательный
Например, Энди (Andie Quincey) сменил фамилию: теперь он Andie Anthony.
MySQL 8.1UPDATE FamilyMembers SET member_name = 'Andie Anthony' WHERE member_name = 'Andie Quincey';
| member_id | status | member_name | birthday |
|---|---|---|---|
| 1 | father | Headley Quincey | 1960-05-13T00:00:00.000Z |
| 2 | mother | Flavia Quincey | 1963-02-16T00:00:00.000Z |
| 3 | son | 1983-06-05T00:00:00.000Z | |
| 4 | daughter | Lela Quincey | 1985-06-07T00:00:00.000Z |
| 5 Ещё 4 строки | daughter | Annie Quincey | 1988-04-10T00:00:00.000Z |
Изменение нескольких столбцов
Чтобы изменить сразу несколько столбцов, перечислите пары поле_таблицы = значение после SET через запятую. Например, Энди купил 4 упаковки молока по 25, а в платёж с идентификатором 3 по ошибке записали 5 по 20. Исправим оба столбца одним запросом:
MySQL 8.1UPDATE Payments SET amount = 4, unit_price = 25 WHERE payment_id = 3;
| payment_id | date | family_member | good | amount | unit_price |
|---|---|---|---|---|---|
| 1 | 2005-02-12T00:00:00.000Z | 1 | 1 | 1 | 2000 |
| 2 | 2005-03-23T00:00:00.000Z | 2 | 1 | 1 | 2100 |
| 3 | 2005-05-14T00:00:00.000Z | 3 | 4 | ||
| 4 | 2005-07-22T00:00:00.000Z | 4 | 5 | 1 | 350 |
| 5 Ещё 24 строки | 2005-07-26T00:00:00.000Z | 4 | 7 | 2 | 150 |
Все перечисленные столбцы изменятся в одних и тех же строках — тех, что подходят под условие WHERE.
Вычисляемые значения
Новое значение можно вычислить из старого — например, удвоить все цены:
MySQL 8.1UPDATE Payments SET unit_price = unit_price * 2;
В выражении можно использовать и другие столбцы той же строки, если их типы совместимы. Например, запишем платёж с идентификатором 3 одной позицией: в unit_price окажется стоимость всей покупки, а в amount — единица.
MySQL 8.1UPDATE Payments SET unit_price = unit_price * amount, amount = 1 WHERE payment_id = 3;
В однотабличном UPDATE присваивания в SET выполняются слева направо, и следующее выражение уже видит новое значение столбца. Если поменять присваивания местами, то unit_price умножится на уже записанную единицу и не изменится:
MySQL 8.1UPDATE Payments SET amount = 1, unit_price = unit_price * amount WHERE payment_id = 3;
Поэтому столбец, который участвует в вычислении других, меняйте последним.
Все выражения в SET вычисляются по значениям строки до изменения, поэтому порядок присваиваний не важен. Такой запрос даст тот же результат:
MySQL 8.1UPDATE Payments SET amount = 1, unit_price = unit_price * amount WHERE payment_id = 3;
Обновление по данным другой таблицы
Иногда строки для обновления нужно выбрать по данным из другой таблицы. Например, выяснилось, что во всех платежах за икру цена записана вдвое меньше настоящей. Самый простой способ — подзапрос в WHERE:
MySQL 8.1UPDATE Payments SET unit_price = unit_price * 2 WHERE good IN ( SELECT good_id FROM Goods WHERE good_name LIKE '%caviar%' );
То же самое можно сделать, соединив таблицы с помощью JOIN прямо в запросе UPDATE:
UPDATE имя_таблицы_1
JOIN имя_таблицы_2 ON условие_соединения
SET имя_таблицы_1.поле_таблицы = значение [, ...]
[WHERE условие_выборки];- [ ]необязательная часть
- ...можно повторить
- имя_таблицы_1
- Таблица, в которой меняются записи.
- имя_таблицы_2
Присоединяемая таблица, по данным которой отбираются строки и вычисляются новые значения.
- условие_соединения
Как связаны строки двух таблиц, например имя_таблицы_1.поле = имя_таблицы_2.поле.
- условие_выборки
Какие строки изменить. В условии можно проверять столбцы обеих таблиц.
Необязательный
MySQL 8.1UPDATE Payments JOIN Goods ON Payments.good = Goods.good_id SET Payments.unit_price = Payments.unit_price * 2 WHERE Goods.good_name LIKE '%caviar%';
То же самое можно сделать, перечислив дополнительные таблицы после FROM:
UPDATE имя_таблицы_1
SET поле_таблицы = значение [, ...]
FROM имя_таблицы_2
WHERE условие_соединения
[AND условие_выборки];- [ ]необязательная часть
- ...можно повторить
- имя_таблицы_1
Таблица, в которой меняются записи. Повторять её после FROM не нужно.
- поле_таблицы
Столбец таблицы имя_таблицы_1. Имя таблицы перед ним не указывается.
- имя_таблицы_2
Дополнительная таблица, по данным которой отбираются строки и вычисляются новые значения.
- условие_соединения
Как связаны строки двух таблиц, например имя_таблицы_1.поле = имя_таблицы_2.поле.
- условие_выборки
Дополнительное условие: какие из связанных строк изменить.
Необязательный
MySQL 8.1UPDATE Payments SET unit_price = unit_price * 2 FROM Goods WHERE Payments.good = Goods.good_id AND Goods.good_name LIKE '%caviar%';
В подзапросе нельзя читать ту же таблицу, которую изменяет UPDATE. Например, удвоить цену в платежах дешевле средней так не получится:
MySQL 8.1UPDATE Payments SET unit_price = unit_price * 2 WHERE unit_price < (SELECT AVG(unit_price) FROM Payments);
Запрос завершится ошибкой:
MySQL 8.1ERROR 1093 (HY000): You can't specify target table 'Payments' for update in FROM clause
Обойти ограничение можно, обернув подзапрос в ещё один SELECT. Тогда средняя цена сначала вычисляется и сохраняется во временной таблице, и обновление читает уже её:
MySQL 8.1UPDATE Payments SET unit_price = unit_price * 2 WHERE unit_price < ( SELECT avg_price FROM (SELECT AVG(unit_price) AS avg_price FROM Payments) AS stats );
Типичные ошибки
Забытый WHERE
Запрос без условия изменит все строки таблицы. Сначала проверьте условие запросом SELECT, а потом подставьте его в UPDATE.
AND вместо запятой в SET
Запись ниже не меняет два столбца: всё, что стоит справа от первого =, считается одним логическим выражением.
MySQL 8.1UPDATE Payments SET amount = 4 AND unit_price = 25 WHERE payment_id = 3;
Запрос выполнится без ошибки и запишет в amount результат этого выражения — 0 или 1, а unit_price не изменится.
Запрос завершится ошибкой:
MySQL 8.1ERROR: argument of AND must be type boolean, not type integer
Чтобы изменить оба столбца, присваивания разделяют запятой. По ней SET понимает, где заканчивается одно и начинается следующее:
MySQL 8.1UPDATE Payments SET amount = 4, unit_price = 25 WHERE payment_id = 3;
Сравнение с NULL через =
Условие WHERE поле = NULL не подходит ни одной строке, поэтому запрос ничего не изменит. Для проверки на пустое значение используйте IS NULL.
Проверьте себя
У некоторых пользователей в таблице Users не указана дата подтверждения почты: поле email_verified_at равно NULL. Сколько строк изменит этот запрос?
MySQL 8.1UPDATE Users SET email_verified_at = '2024-01-01' WHERE email_verified_at = NULL;