Updating data, UPDATE operator
Data that has been added does not always stay correct: people change their surnames, typos turn up in payments, prices need to be recalculated. To fix a row without deleting it, use the UPDATE operator.
General query structure with the UPDATE operator
UPDATE table_name
SET table_field = value [, ...]
[WHERE condition];- [ ]optional part
- ...can be repeated
- table_name
- The table whose records are changed.
- table_field
The column that gets a new value. You can list several table_field = value pairs separated by commas.
- value
The new value: a number, a string, an expression like price * 1.1, or NULL.
- condition
Which rows to change. Without WHERE, every row of the table is changed.
Optional
For example, Andie (Andie Quincey) has changed his surname and is now 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 more rows | daughter | Annie Quincey | 1988-04-10T00:00:00.000Z |
Changing several columns
To change several columns at once, list the table_field = value pairs after SET, separated by commas. For example, Andie bought 4 cartons of milk at 25 each, but the payment with ID 3 was recorded by mistake as 5 at 20. Let's fix both columns in one query:
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 more rows | 2005-07-26T00:00:00.000Z | 4 | 7 | 2 | 150 |
All the listed columns change in the same rows — the ones that match the WHERE condition.
Calculated values
A new value can be calculated from the old one — for example, to double all prices:
MySQL 8.1UPDATE Payments SET unit_price = unit_price * 2;
The expression can also use other columns of the same row, as long as their types are compatible. For example, let's record the payment with ID 3 as a single item: unit_price will hold the cost of the whole purchase, and amount will be one.
MySQL 8.1UPDATE Payments SET unit_price = unit_price * amount, amount = 1 WHERE payment_id = 3;
In a single-table UPDATE, assignments in SET are performed from left to right, and the next expression already sees the new value of the column. If you swap the assignments, unit_price is multiplied by the 1 just written to amount and does not change:
MySQL 8.1UPDATE Payments SET amount = 1, unit_price = unit_price * amount WHERE payment_id = 3;
So put the assignment to a column that other expressions depend on at the end of the list.
All expressions in SET are calculated from the values the row had before the change, so the order of assignments does not matter. This query gives the same result:
MySQL 8.1UPDATE Payments SET amount = 1, unit_price = unit_price * amount WHERE payment_id = 3;
Updating based on data from another table
Sometimes the rows to update have to be selected using data from another table. For example, it turned out that every caviar payment was recorded at half the real price. The simplest way is a subquery in WHERE:
MySQL 8.1UPDATE Payments SET unit_price = unit_price * 2 WHERE good IN ( SELECT good_id FROM Goods WHERE good_name LIKE '%caviar%' );
You can do the same by joining the tables with JOIN right in the UPDATE query:
UPDATE table_name_1
JOIN table_name_2 ON join_condition
SET table_name_1.table_field = value [, ...]
[WHERE condition];- [ ]optional part
- ...can be repeated
- table_name_1
- The table whose records are changed.
- table_name_2
The joined table whose data helps select the rows and calculate the new values.
- join_condition
How the rows are linked, for example table_name_1.field = table_name_2.field.
- condition
Which rows to change. The condition can check columns of both tables.
Optional
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%';
You can do the same by listing the additional tables after FROM:
UPDATE table_name_1
SET table_field = value [, ...]
FROM table_name_2
WHERE join_condition
[AND condition];- [ ]optional part
- ...can be repeated
- table_name_1
The table whose records are changed. Do not repeat it after FROM.
- table_field
A column of table_name_1. It is written without the table name.
- table_name_2
An additional table whose data helps select the rows and calculate the new values.
- join_condition
How the rows are linked, for example table_name_1.field = table_name_2.field.
- condition
An extra condition: which of the linked rows to change.
Optional
MySQL 8.1UPDATE Payments SET unit_price = unit_price * 2 FROM Goods WHERE Payments.good = Goods.good_id AND Goods.good_name LIKE '%caviar%';
A subquery cannot read the same table that UPDATE changes. For example, you cannot double the price in payments cheaper than average this way:
MySQL 8.1UPDATE Payments SET unit_price = unit_price * 2 WHERE unit_price < (SELECT AVG(unit_price) FROM Payments);
The query fails with an error:
MySQL 8.1ERROR 1093 (HY000): You can't specify target table 'Payments' for update in FROM clause
You can work around this by wrapping the subquery in one more SELECT. The average price is then first calculated and stored in a temporary table, and the update reads from it:
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 );
Common mistakes
Forgotten WHERE
A query without a condition changes every row of the table. Check the condition with a SELECT query first, and then put it into UPDATE.
AND instead of a comma in SET
The query below does not change two columns: everything to the right of the first = is treated as a single logical expression.
MySQL 8.1UPDATE Payments SET amount = 4 AND unit_price = 25 WHERE payment_id = 3;
The query runs without an error and writes the result of this expression — 0 or 1 — to amount, while unit_price stays the same.
The query fails with an error:
MySQL 8.1ERROR: argument of AND must be type boolean, not type integer
To change both columns, separate the assignments with a comma. That is how SET knows where one ends and the next one begins:
MySQL 8.1UPDATE Payments SET amount = 4, unit_price = 25 WHERE payment_id = 3;
Comparing with NULL using =
The condition WHERE field = NULL matches no rows, so the query changes nothing. To check for an empty value, use IS NULL.
Test yourself
Some users in the Users table have no email confirmation date: their email_verified_at field is NULL. How many rows will this query change?
MySQL 8.1UPDATE Users SET email_verified_at = '2024-01-01' WHERE email_verified_at = NULL;