MySQL UPDATE statement: syntax, JOINs, and common errors
UPDATE syntax, updating from a JOIN, and the three errors people hit most: 1175, 1093, and 1221.
Quick answer
The MySQL UPDATE statement changes existing rows. Name the table, set the new values, and filter with WHERE:
Leave out WHERE and every row in the table is updated. Everything else in this guide builds on that one form. Every example was run on MySQL 8.4, and the output is copied from the mysql client.
MySQL UPDATE statement syntax
The examples use three small tables:
If you need to load rows first, see MySQL INSERT.
Update one row with WHERE
Filtering on the primary key is the safest form. It touches exactly one row, and MySQL finds it through the index rather than scanning the table.
Before running an UPDATE with a broader filter, run a SELECT with the same WHERE clause. If the row count surprises you, the UPDATE would have too.
Update multiple columns
Separate assignments with commas. Expressions can refer to the current value of the column:
The same idea runs in SQLite, so you can try it here. Change the WHERE clause and see which rows move:
Assignments run left to right
In a single-table UPDATE, MySQL evaluates assignments from left to right, and a later assignment sees the value set by an earlier one. PostgreSQL and the SQL standard do not work this way, so this catches people moving between the two:
The price started at 29.00. stock got 30, the new price, not 29. The MySQL manual says single-table assignments are "generally evaluated from left to right" and that multi-table updates give no ordering guarantee, so do not write logic that depends on the order in a JOIN update.
Rows matched vs rows changed
MySQL only counts a row as affected if its value changes. Run the first update a second time:
The row was found, but the price was already 45.00, so nothing was written. Compare that with a WHERE that matches nothing:
Both report "0 rows affected". Only Rows matched tells you whether the row exists.
Drivers do not all report the same number. The Node.js mysql2 driver, running that same no-op update, returned this:
mysql2 connects with the CLIENT_FOUND_ROWS flag, so its affectedRows is the matched count, and changedRows holds what the mysql client calls "Changed". If your code treats "0 affected rows" as "record not found", check which number your driver returns before relying on it.
Multi-table UPDATE with JOIN
To update rows based on data in another table, join the tables in the UPDATE clause and set columns on the one you want to change:
Ada is the only gold customer, so her two pending orders changed. MySQL puts the join before SET. PostgreSQL uses UPDATE ... FROM instead, which MySQL does not support. For a refresher on join types, see MySQL JOIN.
A multi-table UPDATE can change columns in more than one table at once:
One row in orders and one in customers, reported together as 2.
UPDATE with ORDER BY and LIMIT
A single-table UPDATE accepts ORDER BY and LIMIT, which lets you pick which rows get changed:
The two lowest-stock products (Desk Mat at 0, SQL Handbook at 30) each gained 10.
LIMIT is also how you break a large update into batches. Run the statement repeatedly until it reports 0 rows affected, so no single transaction holds locks on millions of rows. The same pattern works for MySQL DELETE.
Error 1221: ORDER BY or LIMIT on a multi-table UPDATE
Neither clause is allowed once a JOIN is involved:
With ORDER BY instead, the message is ERROR 1221 (HY000): Incorrect usage of UPDATE and ORDER BY. To batch a join update, pick the target keys in a derived table that has its own ORDER BY and LIMIT, then join to it:
Batching on a primary key range (WHERE o.id BETWEEN 1 AND 10000) works too.
Work With Your Databases Like A Pro
Query, explore, and manage your databases with a beautiful desktop app and built-in AI.
Download Now
Safe updates mode: error 1175
With sql_safe_updates turned on, MySQL refuses an UPDATE that could touch every row in the table:
A WHERE clause alone is not enough. It has to use an indexed column, or the statement needs a LIMIT:
category has no index, so the first statement fails. Adding LIMIT or filtering on the primary key gets through.
Safe updates mode is off by default in the server. You usually meet it because a client turned it on: MySQL Workbench enables it by default in its SQL editor (the "Safe Updates" option under Preferences, SQL Editor), and the mysql command-line client enables it with --safe-updates (or its alias, --i-am-a-dummy).
The fix depends on intent:
- If you meant to touch specific rows, add a
WHEREon an indexed column. This is the right answer most of the time. - If you meant to update every row, turn it off for the session with
SET SESSION sql_safe_updates = 0;, run the statement, and turn it back on.
Error 1093: UPDATE with a subquery on the same table
A common request: cut the price of anything priced above average. The obvious query fails:
MySQL does not let you read from the table you are updating in a subquery, because the rows could change underneath the read. The same error appears with WHERE id IN (SELECT id FROM products WHERE ...).
Workaround 1: wrap the subquery in a derived table
Put the subquery inside another SELECT with an alias. MySQL materializes the derived table first, so the read is finished before the update starts:
Only Monitor was above the average, and its price went from 207.90 to 187.11.
Workaround 2: JOIN to a derived table
For anything more involved, join to the derived table. This is the approach the MySQL manual recommends, and it reads better when you need several values from the subquery. Here the most expensive product in each category gets a label:
UPDATE from a subquery on another table
The 1093 restriction only applies to the table being updated. A correlated subquery against a different table works directly:
This is the rows-matched distinction again. Ada and Grace both qualified, but Ada was already gold, so only Grace changed.
Common mistakes
Forgetting WHERE. UPDATE products SET price = 0; changes every row and the only undo is a backup or an open transaction. Run risky updates inside START TRANSACTION, check the result with a SELECT, then COMMIT or ROLLBACK.
Reading "0 rows affected" as "not found". As shown above, a row that already holds the new value reports 0 changed. Check Rows matched, or your driver's equivalent.
Updating a column that other assignments read. Because single-table assignments run left to right, SET a = b, b = a does not swap the columns in MySQL. Both end up with the original value of b.
Unindexed filters on big tables. An UPDATE with a WHERE on an unindexed column scans the table and, in InnoDB, can lock far more rows than it changes. Index the filter column or batch on the primary key.
Quick reference
| Task | Statement |
|---|---|
| Update one row | UPDATE t SET col = val WHERE id = 1; |
| Update several columns | UPDATE t SET a = 1, b = b + 1 WHERE ...; |
| Update the first N rows by an order | UPDATE t SET ... ORDER BY col LIMIT n; |
| Update from another table | UPDATE t JOIN u ON ... SET t.col = u.col WHERE ...; |
| Subquery on the same table | Wrap it in a derived table: (SELECT ... FROM (SELECT ...) AS x) |
| Turn off safe updates for a session | SET SESSION sql_safe_updates = 0; |
Related guides: MySQL DELETE uses the same WHERE, ORDER BY, and LIMIT rules, and MySQL JOIN covers the join types you can use in a multi-table UPDATE.