MySQL UPDATE and DELETE
Change a row by reading its current value in the same statement, then remove one. Both start with the `SELECT` that proves the `WHERE` clause matches what you think it does.
Schemas and Data Guide 8 of 45 Beginner
- OSUbuntu 26.04 LTS (resolute)
- MySQL8.4.10-0ubuntu0.26.04.1
- EngineInnoDB (default)
- TimeAbout 12 min
- Reviewed27 August 2026
Written against the versions above. Nothing here is version-specific. The habit in step one is the whole guide.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| db-a01 | 192.168.0.81 | Ubuntu 26.04 LTS | Primary / Source / Replica Set Member 1 | 2 Core | 4 GB | 50 GB |
Before you start
- The
customerstable with rows from the previous two guides.
-
Update a value using the value it already has
SET credit = credit + 10reads and writes in one statement. The arithmetic happens on the server, atomically, so two concurrent updates cannot both read the same starting figure and lose one of the increments.Doing this in application code - select, add ten, update - is the classic lost update, and it is invisible in testing because it needs two requests at once.
Grace started on
0.00, the column default from the first insert.bash sudo mysql appdb -e "UPDATE customers SET credit = credit + 10 WHERE email = 'grace@example.com'"sudo mysql appdb -e "UPDATE customers SET credit = credit + 10 WHERE email = 'grace@example.com'"Expected resultNo output from the update, then Grace's credit reading
10.00.Success conditionYou can modify a value relative to itself without a read-modify-write race.
-
Delete one row, and confirm which
DELETEwith aWHEREon the primary key, then the sameSELECTagain to prove what changed.The habit worth building is running the
SELECTbefore theDELETEwith the identicalWHEREclause. It costs one command and it is the only way to know the clause matches what you intend. ADELETEwith a mistypedWHEREis not undoable outside a transaction or a backup.Row 4 was the row that failed to insert earlier; row 5 is the Unicode one, which survives.
bash Example session sudo mysql appdb -e "DELETE FROM customers WHERE id = 4"sudo mysql --table appdb -e "SELECT id, name FROM customers"+----+---------------------+| id | name |+----+---------------------+| 1 | Ada Lovelace || 2 | Grace Hopper || 3 | Katsuko Saruhashi || 5 | Zoë Washburne 🚀 |+----+---------------------+Expected resultThe remaining rows, without id 4.
Success conditionYou have removed a specific row and verified the result.
-
Ask how many rows an update actually changed
ROW_COUNT()reports the effect of the statement before it. Run the sameUPDATEtwice and the answer differs.The first run sets
statustosuspendedon every customer with zero credit. The second run matches the same rows and reports 0, because they already hold that value. MySQL counts rows *changed*, not rows *matched*.That distinction is why "0 rows affected" on a migration is not proof the
WHEREwas wrong - it may mean the work was already done. Which of the two it is decides whether you investigate or move on.bash Example session sudo mysql appdb -e "UPDATE customers SET status = 'suspended' WHERE credit = 0; SELECT ROW_COUNT() AS rows_changed"rows_changed1sudo mysql --table appdb -e "SELECT id, name, status, credit FROM customers"+----+---------------------+-----------+--------+| id | name | status | credit |+----+---------------------+-----------+--------+| 1 | Ada Lovelace | active | 120.50 || 2 | Grace Hopper | active | 10.00 || 3 | Katsuko Saruhashi | active | 75.25 || 5 | Zoë Washburne 🚀 | suspended | 0.00 |+----+---------------------+-----------+--------+sudo mysql appdb -e "UPDATE customers SET status = 'suspended' WHERE credit = 0; SELECT ROW_COUNT() AS rows_changed"rows_changed0Expected resultA non-zero count, the updated rows, then
0from the identical rerun.Success conditionYou can tell an update that did nothing from one that matched nothing.
-
Let the client stop you deleting a whole table
--safe-updatesrefuses anyUPDATEorDELETEwhoseWHEREclause does not use a key column. Here the clause filters onstatus, which is not indexed.ERROR 1175, and nothing was deleted. The mode is blunt - it would refuse a perfectly good statement too - and that is the trade. It exists becauseDELETE FROM customersis valid SQL that empties the table, and because the most expensive incidents start with aWHEREclause that was fine in testing.Worth turning on in your own client config for any session against production.
bash Example session sudo mysql --safe-updates appdb -e "DELETE FROM customers WHERE status = 'suspended'" ; echo "exit=$?"ERROR 1175 (HY000) at line 1: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column.exit=1Expected result
ERROR 1175 (HY000): You are using safe update modeandexit=1.Success conditionYou have a client-side guard against an unqualified delete.
-
Undo a delete, properly
The real answer to "I deleted the wrong rows". Four statements in one connection: begin, delete every order, count, roll back, count again.
after_deleteis 0 andafter_rollbackis 3. The rows were genuinely gone as far as this session was concerned, andROLLBACKbrought all three back. Nothing else on the server ever saw them missing.This is InnoDB being transactional, and it is the habit that makes destructive work survivable:
START TRANSACTION, run the statement, check the count, and only thenCOMMIT. Note it must be one connection - a newmysqlinvocation is a new session and an open transaction would be rolled back on disconnect.bash Example session sudo mysql appdb -e "START TRANSACTION; DELETE FROM orders; SELECT COUNT(*) AS after_delete FROM orders; ROLLBACK; SELECT COUNT(*) AS after_rollback FROM orders"after_delete0after_rollback3Expected result
after_delete0, thenafter_rollback3.Success conditionYou can make a destructive statement reversible before you run it.
Troubleshooting
An
UPDATEreports 0 rows changed but no error.Why: The
WHEREmatched nothing, or matched rows already holding that value.Fix:Run the
WHEREas aSELECTfirst. MySQL distinguishes rows matched from rows changed and the client only shows one of them.ERROR 1175- safe update mode.Why: The client is in
--safe-updates, which refuses UPDATE and DELETE without a key in the WHERE clause.Fix:Add the key. The mode exists because
DELETE FROM customerswith no WHERE is a valid statement that empties the table.You deleted the wrong rows.
Why: The
WHEREclause did not mean what you thought.Fix:Restore from backup - the backup track covers this. There is no undo, which is why the SELECT-first habit is worth the extra command.