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

MySQL 8.1
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.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 more rows
daughterAnnie Quincey1988-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.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 more rows
2005-07-26T00:00:00.000Z472150

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.1
UPDATE 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.1
UPDATE 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.1
UPDATE 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.1
UPDATE 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.1
UPDATE 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:

MySQL 8.1
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.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%';

You can do the same by listing the additional tables after FROM:

MySQL 8.1
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.1
UPDATE 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.1
UPDATE Payments
SET unit_price = unit_price * 2
WHERE unit_price < (SELECT AVG(unit_price) FROM Payments);

The query fails with an error:

MySQL 8.1
ERROR 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.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
);

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.1
UPDATE 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.1
ERROR: 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.1
UPDATE 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.1
UPDATE Users
SET email_verified_at = '2024-01-01'
WHERE email_verified_at = NULL;