Урок доступен в видеоформате 

Обновление данных, оператор UPDATE

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

Общая структура запроса с оператором UPDATE

MySQL 8.1
UPDATE имя_таблицы
SET поле_таблицы = значение [, ...]
[WHERE условие_выборки];
  • [ ]необязательная часть
  • ...можно повторить
имя_таблицы
Таблица, в которой меняются записи.
поле_таблицы

Столбец, которому присваивается новое значение. Пар поле_таблицы = значение может быть несколько, через запятую.

значение

Новое значение: число, строка, выражение вроде price * 1.1 или NULL.

условие_выборки

Какие строки изменить. Без WHERE изменятся все строки таблицы.

Необязательный

Например, Энди (Andie Quincey) сменил фамилию: теперь он Andie Anthony.

MySQL 8.1
UPDATE FamilyMembers
SET member_name = 'Andie Anthony'
WHERE member_name = 'Andie Quincey';
member_idstatusmember_namebirthday
1fatherHeadley Quincey1960-05-13T00:00:00.000Z
2motherFlavia Quincey1963-02-16T00:00:00.000Z
3sonAndie QuinceyAndie Anthony1983-06-05T00:00:00.000Z
4daughterLela Quincey1985-06-07T00:00:00.000Z
5
Ещё 4 строки
daughterAnnie Quincey1988-04-10T00:00:00.000Z

Изменение нескольких столбцов

Чтобы изменить сразу несколько столбцов, перечислите пары поле_таблицы = значение после SET через запятую. Например, Энди купил 4 упаковки молока по 25, а в платёж с идентификатором 3 по ошибке записали 5 по 20. Исправим оба столбца одним запросом:

MySQL 8.1
UPDATE Payments
SET amount = 4, unit_price = 25
WHERE payment_id = 3;
payment_iddatefamily_membergoodamountunit_price
12005-02-12T00:00:00.000Z1112000
22005-03-23T00:00:00.000Z2112100
32005-05-14T00:00:00.000Z34542025
42005-07-22T00:00:00.000Z451350
5
Ещё 24 строки
2005-07-26T00:00:00.000Z472150

Все перечисленные столбцы изменятся в одних и тех же строках — тех, что подходят под условие WHERE.

Вычисляемые значения

Новое значение можно вычислить из старого — например, удвоить все цены:

MySQL 8.1
UPDATE Payments
SET unit_price = unit_price * 2;

В выражении можно использовать и другие столбцы той же строки, если их типы совместимы. Например, запишем платёж с идентификатором 3 одной позицией: в unit_price окажется стоимость всей покупки, а в amount — единица.

MySQL 8.1
UPDATE Payments
SET unit_price = unit_price * amount,
    amount = 1
WHERE payment_id = 3;

В однотабличном UPDATE присваивания в SET выполняются слева направо, и следующее выражение уже видит новое значение столбца. Если поменять присваивания местами, то unit_price умножится на уже записанную единицу и не изменится:

MySQL 8.1
UPDATE Payments
SET amount = 1,
    unit_price = unit_price * amount
WHERE payment_id = 3;

Поэтому столбец, который участвует в вычислении других, меняйте последним.

Все выражения в SET вычисляются по значениям строки до изменения, поэтому порядок присваиваний не важен. Такой запрос даст тот же результат:

MySQL 8.1
UPDATE Payments
SET amount = 1,
    unit_price = unit_price * amount
WHERE payment_id = 3;

Обновление по данным другой таблицы

Иногда строки для обновления нужно выбрать по данным из другой таблицы. Например, выяснилось, что во всех платежах за икру цена записана вдвое меньше настоящей. Самый простой способ — подзапрос в WHERE:

MySQL 8.1
UPDATE Payments
SET unit_price = unit_price * 2
WHERE good IN (
    SELECT good_id
    FROM Goods
    WHERE good_name LIKE '%caviar%'
);

То же самое можно сделать, соединив таблицы с помощью JOIN прямо в запросе UPDATE:

MySQL 8.1
UPDATE имя_таблицы_1
JOIN имя_таблицы_2 ON условие_соединения
SET имя_таблицы_1.поле_таблицы = значение [, ...]
[WHERE условие_выборки];
  • [ ]необязательная часть
  • ...можно повторить
имя_таблицы_1
Таблица, в которой меняются записи.
имя_таблицы_2

Присоединяемая таблица, по данным которой отбираются строки и вычисляются новые значения.

условие_соединения

Как связаны строки двух таблиц, например имя_таблицы_1.поле = имя_таблицы_2.поле.

условие_выборки

Какие строки изменить. В условии можно проверять столбцы обеих таблиц.

Необязательный
MySQL 8.1
UPDATE Payments
JOIN Goods ON Payments.good = Goods.good_id
SET Payments.unit_price = Payments.unit_price * 2
WHERE Goods.good_name LIKE '%caviar%';

То же самое можно сделать, перечислив дополнительные таблицы после FROM:

MySQL 8.1
UPDATE имя_таблицы_1
SET поле_таблицы = значение [, ...]
FROM имя_таблицы_2
WHERE условие_соединения
[AND условие_выборки];
  • [ ]необязательная часть
  • ...можно повторить
имя_таблицы_1

Таблица, в которой меняются записи. Повторять её после FROM не нужно.

поле_таблицы

Столбец таблицы имя_таблицы_1. Имя таблицы перед ним не указывается.

имя_таблицы_2

Дополнительная таблица, по данным которой отбираются строки и вычисляются новые значения.

условие_соединения

Как связаны строки двух таблиц, например имя_таблицы_1.поле = имя_таблицы_2.поле.

условие_выборки

Дополнительное условие: какие из связанных строк изменить.

Необязательный
MySQL 8.1
UPDATE Payments
SET unit_price = unit_price * 2
FROM Goods
WHERE Payments.good = Goods.good_id
    AND Goods.good_name LIKE '%caviar%';

В подзапросе нельзя читать ту же таблицу, которую изменяет UPDATE. Например, удвоить цену в платежах дешевле средней так не получится:

MySQL 8.1
UPDATE Payments
SET unit_price = unit_price * 2
WHERE unit_price < (SELECT AVG(unit_price) FROM Payments);

Запрос завершится ошибкой:

MySQL 8.1
ERROR 1093 (HY000): You can't specify target table 'Payments' for update in FROM clause

Обойти ограничение можно, обернув подзапрос в ещё один SELECT. Тогда средняя цена сначала вычисляется и сохраняется во временной таблице, и обновление читает уже её:

MySQL 8.1
UPDATE 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.1
UPDATE Payments
SET amount = 4 AND unit_price = 25
WHERE payment_id = 3;

Запрос выполнится без ошибки и запишет в amount результат этого выражения — 0 или 1, а unit_price не изменится.

Запрос завершится ошибкой:

MySQL 8.1
ERROR: argument of AND must be type boolean, not type integer

Чтобы изменить оба столбца, присваивания разделяют запятой. По ней SET понимает, где заканчивается одно и начинается следующее:

MySQL 8.1
UPDATE Payments
SET amount = 4, unit_price = 25
WHERE payment_id = 3;

Сравнение с NULL через =

Условие WHERE поле = NULL не подходит ни одной строке, поэтому запрос ничего не изменит. Для проверки на пустое значение используйте IS NULL.

Проверьте себя

У некоторых пользователей в таблице Users не указана дата подтверждения почты: поле email_verified_at равно NULL. Сколько строк изменит этот запрос?

MySQL 8.1
UPDATE Users
SET email_verified_at = '2024-01-01'
WHERE email_verified_at = NULL;