Always name the columns you are filling. The statement keeps working when someone adds a column later, and readers can see which value goes where. Several rows can go in one statement:
13. INSERT, UPDATE, DELETE and transactions
Step 1 of 4 · Lesson
Lesson 13 of 21
MySQL INSERT, UPDATE, DELETE and transactions (COMMIT and ROLLBACK)
Change data safely: INSERT rows, UPDATE and DELETE with a previewed WHERE, delete child rows first, and wrap changes in a transaction.
In short
How do you safely update data in MySQL?
Preview first with a SELECT using the exact WHERE you plan to use and note the row count. Then run START TRANSACTION, the UPDATE, and check ROW_COUNT() and the changed rows. If the count and values are right, COMMIT; otherwise ROLLBACK and nothing is changed.
By the end of this lesson you can
- Insert one or many rows, with and without a column list, and copy rows with INSERT … SELECT
- Update and delete only the intended rows by previewing the WHERE clause
- Delete child rows before parents when foreign keys protect them
- Use START TRANSACTION, COMMIT and ROLLBACK, and check ROW_COUNT()
INSERT: add rows
INSERT INTO customers (customer_id, customer_name, city, email) VALUES
(7, 'Gita', 'Ludhiana', NULL),
(8, 'Harpreet', 'Ludhiana', 'harpreet@example.test');
SELECT * FROM customers WHERE city = 'Ludhiana';| customer_id | customer_name | city | |
|---|---|---|---|
| 7 | Gita | Ludhiana | NULL |
| 8 | Harpreet | Ludhiana | harpreet@example.test |
INSERT … SELECT copies rows from a query instead of typing values — useful for archive tables and loading staging data: INSERT INTO archive_orders SELECT * FROM orders WHERE order_date < '2026-02-01';.
UPDATE: change existing rows
UPDATE table SET column = value WHERE condition. The WHERE decides which rows change — leave it out and every row changes. Professionals preview first with the identical WHERE:
-- 1. preview: exactly one row?
SELECT product_id, product_name, price FROM products WHERE product_id = 4;
-- 2. change it
UPDATE products SET price = price * 1.10 WHERE product_id = 4;
-- 3. verify
SELECT product_id, product_name, price FROM products WHERE product_id = 4;| product_id | product_name | price |
|---|---|---|
| 4 | Mug | 250.00 |
| product_id | product_name | price |
|---|---|---|
| 4 | Mug | 275.00 |
Mug goes from 250.00 to 275.00. SET can change several columns at once: SET price = 275, category = 'Kitchen'.
DELETE: remove rows
DELETE FROM table WHERE condition removes whole rows. Like UPDATE, it needs a previewed WHERE. Foreign keys add one more rule: you cannot delete a parent that still has children. Order 104 has an item, so the item must go first:
DELETE FROM order_items WHERE order_id = 104;
DELETE FROM orders WHERE order_id = 104;
SELECT COUNT(*) AS orders_left FROM orders;| orders_left |
|---|
| 7 |
Deleting the order first fails with error 1451 (Cannot delete or update a parent row). TRUNCATE TABLE empties a whole table quickly, but it is DDL: it cannot be rolled back and fails if other tables reference it.
Transactions: all or nothing
A transaction groups statements so they succeed or fail together. Moving stock, paying an invoice, cancelling an order and its items — half-done is worse than not done.
START TRANSACTION; -- changes are now provisional
… INSERT / UPDATE / DELETE …
COMMIT; -- make them permanent
-- or
ROLLBACK; -- throw them all away
By default MySQL runs in autocommit mode: each statement commits on its own the moment it finishes. START TRANSACTION switches that off until COMMIT or ROLLBACK.
START TRANSACTION;
UPDATE orders SET status = 'completed' WHERE order_id = 107;
SELECT order_id, status FROM orders WHERE order_id = 107;
ROLLBACK;
SELECT order_id, status FROM orders WHERE order_id = 107;| order_id | status |
|---|---|
| 107 | completed |
| order_id | status |
|---|---|
| 107 | pending |
Inside the transaction 107 is completed; after ROLLBACK it is pending again. Other users never saw the provisional change.
The safe change pattern
Use this five-step pattern for every change to data that matters:
- Preview —
SELECT … WHERE <condition>and note the row count. - Begin —
START TRANSACTION; - Change — run the UPDATE / DELETE with the same WHERE.
- Verify —
SELECT ROW_COUNT();right after the change must equal the preview count; re-run the preview to see the new values. - Decide —
COMMIT;if everything matches, otherwiseROLLBACK;.
SELECT COUNT(*) AS will_change FROM products WHERE category = 'Stationery';
START TRANSACTION;
UPDATE products SET price = ROUND(price * 0.95, 2) WHERE category = 'Stationery';
SELECT ROW_COUNT() AS changed;
SELECT product_name, price FROM products WHERE category = 'Stationery' ORDER BY product_id;
ROLLBACK;| will_change |
|---|
| 3 |
| changed |
|---|
| 3 |
| product_name | price |
|---|---|
| Notebook | 95.00 |
| Pen | 19.00 |
| Planner | 285.00 |
Preview says 3, ROW_COUNT says 3, and the new prices look right — this would be safe to COMMIT. We rolled back so the shop stays unchanged for the next lesson.
What ROLLBACK cannot undo
- DDL — CREATE, ALTER, DROP, TRUNCATE — commits automatically, even in the middle of a transaction (lesson 12).
- Tables that are not InnoDB (for example the old MyISAM engine) ignore transactions.
- Anything already committed — including by autocommit before you typed START TRANSACTION.
In the practice editor, the Reset database button restores the original shop whatever you did.
Common mistakes
Wrong
UPDATE products SET price = 0;Fix
Preview with SELECT … WHERE, then UPDATE … WHERE inside a transaction.
Why: No WHERE — every product now costs 0 (and autocommit saved it).
Wrong
DELETE FROM orders WHERE order_id = 104; -- firstFix
Delete the child rows (order_items) first, or design the FK with ON DELETE CASCADE on purpose.
Why: order_items still references the order: error 1451.
Wrong
INSERT INTO customers VALUES ('Gita', 'Ludhiana');Fix
INSERT INTO customers (customer_id, customer_name, city) VALUES (7, 'Gita', 'Ludhiana');Why: Without a column list you must give every column in table order (error 1136).
Wrong
Expecting ROLLBACK to undo an ALTER TABLE
Fix
Keep DDL out of data-fix transactions.
Why: DDL commits implicitly.
Check yourself
- List the five steps of the safe change pattern.
- Why must order_items rows be deleted before their order?
- What does autocommit mean, and how does START TRANSACTION change it?
Answer out loud or in your notebook. If one is hard, reread that section before practising.
Frequently asked questions
How do I undo an UPDATE in MySQL?
Only if it is still inside an open transaction: run ROLLBACK. With autocommit on, a finished UPDATE is permanent and must be reversed with another UPDATE or restored from a backup.
What is MySQL error 1175 (safe update mode)?
MySQL Workbench blocks UPDATE and DELETE statements whose WHERE does not use a key column. Filter by the primary key, or deliberately turn the option off under Edit → Preferences → SQL Editor.
What is the difference between DELETE and TRUNCATE?
DELETE removes chosen rows, can use WHERE and can be rolled back. TRUNCATE empties the whole table, resets AUTO_INCREMENT, is DDL (no rollback) and fails if a foreign key references the table.
Quick quiz — 4 questions
Quick Check: INSERT, UPDATE, DELETE and transactions
Meet Learning Shop
One small fictional shop powers the whole course: 6 customers, 6 products, 8 orders and 14 order lines, small enough to check every answer by eye. The practice editor on this page already contains it. To practise in MySQL Workbench, import it once.
customers.customer_id → orders.customer_id · orders.order_id → order_items.order_id · products.product_id → order_items.product_id
Import in MySQL Workbench
- Connect to your local MySQL server (8.0.16 or newer; 8.4 recommended).
- Choose File → Open SQL Script and open the downloaded file.
- Click the lightning bolt to execute the whole script. The last result shows 6, 6, 8 and 14 rows.
- Refresh the Schemas panel, then select the database:
USE portfolio_retail_lab;
SHOW TABLES;Command line instead: mysql -u root -p < mysql-retail-lab.sql
In the practice editor, the Reset database button does the same as the reset script — instantly and only in your browser.
Let’s do one together
Change one value inside a transaction, check it, then undo it.
- 1Run the script as it is: Notebook is 110.00 inside the transaction and 100.00 after ROLLBACK.
- 2Replace ROLLBACK with COMMIT and run again: the change stays.
- 3Press Reset database to restore the original prices.
- 4Try the five-step pattern on a different product with a preview count first.
START TRANSACTION;
UPDATE products SET price = 110 WHERE product_id = 1;
SELECT product_id, price FROM products WHERE product_id = 1;
ROLLBACK;
SELECT product_id, price FROM products WHERE product_id = 1;What you should see
First result: 1 | 110.00 (inside the transaction). Second result: 1 | 100.00 (after ROLLBACK).
| product_id | price |
|---|---|
| 1 | 110.00 |
| product_id | price |
|---|---|
| 1 | 100.00 |
Practice problems
16 problems from Easy to Professional · 0 solved · J / K to move
Preview the exact row before changing it.
Target: One Notebook row, id 1, price 100. Never run UPDATE without checking the predicate.
Database already selected: Learning Shop (small)(portfolio_retail_lab)— write the query directly, no USE needed.
I’m stuck — show a hint
Use the same WHERE in SELECT as the proposed UPDATE.
Reference answer
SELECT * FROM products WHERE product_id=1;| product_id | product_name | category | price |
|---|---|---|---|
| 1 | Notebook | Stationery | 100.00 |
More than one query can be correct. If your result matches the target, your query is right.
Count how many rows an UPDATE would touch before running it: products in Stationery.
Target: 1 row: rows_to_change = 3
I’m stuck — show a hint
Run the same WHERE as a COUNT first.
Reference answer
SELECT COUNT(*) AS rows_to_change FROM products WHERE category = 'Stationery';| rows_to_change |
|---|
| 3 |
More than one query can be correct. If your result matches the target, your query is right.
Insert a new product 7, 'Eraser', Stationery, 10.00, then list all Stationery products.
Target: 4 rows: Notebook, Pen, Planner, Eraser
I’m stuck — show a hint
INSERT INTO products (product_id, product_name, category, price) VALUES (…);
Reference answer
INSERT INTO products (product_id, product_name, category, price) VALUES (7, 'Eraser', 'Stationery', 10.00); SELECT product_id, product_name, price FROM products WHERE category = 'Stationery' ORDER BY product_id;| product_id | product_name | price |
|---|---|---|
| 1 | Notebook | 100.00 |
| 2 | Pen | 20.00 |
| 6 | Planner | 300.00 |
| 7 | Eraser | 10.00 |
More than one query can be correct. If your result matches the target, your query is right.
Insert customers 7 Gita (Ludhiana, no email) and 8 Harpreet (Ludhiana, harpreet@example.test) in one statement, then list Ludhiana customers.
Target: 2 rows: Gita (email NULL), Harpreet
I’m stuck — show a hint
One INSERT with two value lists.
Reference answer
INSERT INTO customers (customer_id, customer_name, city, email) VALUES (7, 'Gita', 'Ludhiana', NULL), (8, 'Harpreet', 'Ludhiana', 'harpreet@example.test'); SELECT customer_id, customer_name, email FROM customers WHERE city = 'Ludhiana' ORDER BY customer_id;| customer_id | customer_name | |
|---|---|---|
| 7 | Gita | NULL |
| 8 | Harpreet | harpreet@example.test |
More than one query can be correct. If your result matches the target, your query is right.
Inside a transaction, change Pen's price to 25, check it, then ROLLBACK.
Target: 25.00 inside; 20.00 after ROLLBACK
I’m stuck — show a hint
START TRANSACTION … ROLLBACK.
Reference answer
START TRANSACTION; UPDATE products SET price = 25 WHERE product_id = 2; SELECT price FROM products WHERE product_id = 2; ROLLBACK;| price |
|---|
| 25.00 |
More than one query can be correct. If your result matches the target, your query is right.
Insert a new customer 7 'Gita', Ludhiana, then roll it back.
Target: 7 inside; 6 after ROLLBACK
I’m stuck — show a hint
INSERT INTO customers VALUES (...).
Reference answer
START TRANSACTION; INSERT INTO customers VALUES (7, 'Gita', 'Ludhiana', 'gita@example.test'); SELECT COUNT(*) FROM customers; ROLLBACK;| COUNT(*) |
|---|
| 7 |
More than one query can be correct. If your result matches the target, your query is right.
Record a new pending order 109 for Farah (customer 6) dated 2026-03-15, then show Farah's orders.
Target: 1 row: 109, 2026-03-15, pending
I’m stuck — show a hint
Name the columns in the INSERT.
Reference answer
INSERT INTO orders (order_id, customer_id, order_date, status) VALUES (109, 6, '2026-03-15', 'pending'); SELECT order_id, order_date, status FROM orders WHERE customer_id = 6;| order_id | order_date | status |
|---|---|---|
| 109 | 2026-03-15 | pending |
More than one query can be correct. If your result matches the target, your query is right.
Raise the Mug's price by 10% and show its new price.
Target: 1 row: Mug 275.00
I’m stuck — show a hint
UPDATE products SET price = price * 1.10 WHERE product_id = 4;
Reference answer
UPDATE products SET price = price * 1.10 WHERE product_id = 4; SELECT product_name, price FROM products WHERE product_id = 4;| product_name | price |
|---|---|
| Mug | 275.00 |
More than one query can be correct. If your result matches the target, your query is right.
Cancel pending order 107, then show how many orders have each status.
Target: 2 rows: cancelled 2, completed 6
I’m stuck — show a hint
UPDATE … WHERE order_id = 107 AND status = 'pending'; then GROUP BY status.
Reference answer
UPDATE orders SET status = 'cancelled' WHERE order_id = 107 AND status = 'pending'; SELECT status, COUNT(*) AS orders FROM orders GROUP BY status ORDER BY status;| status | orders |
|---|---|
| cancelled | 2 |
| completed | 6 |
More than one query can be correct. If your result matches the target, your query is right.
Change an order status and roll it back.
Target: Order 107 is completed inside the transaction and pending afterwards.
I’m stuck — show a hint
Use START TRANSACTION and ROLLBACK; do not commit.
Reference answer
START TRANSACTION;
UPDATE orders SET status='completed' WHERE order_id=107;
SELECT status FROM orders WHERE order_id=107;
ROLLBACK;
SELECT status FROM orders WHERE order_id=107;| status |
|---|
| pending |
More than one query can be correct. If your result matches the target, your query is right.
Give Electronics a 10% price cut inside a transaction, show the new prices, roll back.
Target: Desk lamp 720, Keyboard 1080 inside; 800 / 1200 after
I’m stuck — show a hint
UPDATE ... SET price = price * 0.9.
Reference answer
START TRANSACTION; UPDATE products SET price = price * 0.9 WHERE category = 'Electronics'; SELECT product_name, price FROM products WHERE category = 'Electronics' ORDER BY product_id; ROLLBACK;| product_name | price |
|---|---|
| Desk lamp | 720.00 |
| Keyboard | 1080.00 |
More than one query can be correct. If your result matches the target, your query is right.
Delete the order items that belong to cancelled orders (use a subquery), then count the remaining items.
Target: 1 row: items_left = 13
I’m stuck — show a hint
DELETE FROM order_items WHERE order_id IN (SELECT order_id FROM orders WHERE status = 'cancelled');
Reference answer
DELETE FROM order_items WHERE order_id IN (SELECT order_id FROM orders WHERE status = 'cancelled'); SELECT COUNT(*) AS items_left FROM order_items;| items_left |
|---|
| 13 |
More than one query can be correct. If your result matches the target, your query is right.
Give every Stationery product 5% off, rounded to 2 decimals, and list the new Stationery prices.
Target: 3 rows: Notebook 95.00, Pen 19.00, Planner 285.00
I’m stuck — show a hint
UPDATE … SET price = ROUND(price * 0.95, 2) WHERE category = 'Stationery'.
Reference answer
UPDATE products SET price = ROUND(price * 0.95, 2) WHERE category = 'Stationery'; SELECT product_name, price FROM products WHERE category = 'Stationery' ORDER BY product_id;| product_name | price |
|---|---|
| Notebook | 95.00 |
| Pen | 19.00 |
| Planner | 285.00 |
More than one query can be correct. If your result matches the target, your query is right.
Delete the cancelled order 104 inside a transaction. Why must its items go first?
Target: 7 orders inside; 8 after ROLLBACK
I’m stuck — show a hint
Child rows (order_items) reference the order.
Reference answer
START TRANSACTION; DELETE FROM order_items WHERE order_id = 104; DELETE FROM orders WHERE order_id = 104; SELECT COUNT(*) FROM orders; ROLLBACK;| COUNT(*) |
|---|
| 7 |
More than one query can be correct. If your result matches the target, your query is right.
Remove cancelled order 104 for good: in one transaction delete its items and the order, verify the counts, and COMMIT.
Target: 1 row: orders_left = 7, items_left = 13 (and the change is committed)
I’m stuck — show a hint
Child rows first; one SELECT with two scalar subqueries for the check; then COMMIT.
Reference answer
START TRANSACTION; DELETE FROM order_items WHERE order_id = 104; DELETE FROM orders WHERE order_id = 104; SELECT (SELECT COUNT(*) FROM orders) AS orders_left, (SELECT COUNT(*) FROM order_items) AS items_left; COMMIT;| orders_left | items_left |
|---|---|
| 7 | 13 |
More than one query can be correct. If your result matches the target, your query is right.
Safe price update: preview, START TRANSACTION, UPDATE Mug to 275, verify exactly 1 row changed, ROLLBACK.
Target: changed = 1; Mug back to 250.00 after ROLLBACK
I’m stuck — show a hint
ROW_COUNT() right after the UPDATE.
Reference answer
SELECT * FROM products WHERE product_id = 4; START TRANSACTION; UPDATE products SET price = 275 WHERE product_id = 4; SELECT ROW_COUNT() AS changed; ROLLBACK;| changed |
|---|
| 1 |
More than one query can be correct. If your result matches the target, your query is right.
Practice mode runs in your browser on a SQLite engine with a MySQL compatibility layer, so you can practise without installing anything. Your result is compared with the reference answer on a fresh copy of the same rows, so any correct query is accepted. A few MySQL-only features (EXPLAIN columns, WITH ROLLUP, user variables) behave differently here — run those in MySQL Workbench.
Finished? Save your queries in a .sql file and mark the lesson complete.
Answers — INSERT, UPDATE, DELETE and transactions
Easy. Preview the exact row before changing it. Target: One Notebook row, id 1, price 100. Never run UPDATE without checking the predicate.
SELECT * FROM products WHERE product_id=1;
Easy. Count how many rows an UPDATE would touch before running it: products in Stationery. Target: 1 row: rows_to_change = 3
SELECT COUNT(*) AS rows_to_change FROM products WHERE category = 'Stationery';
Easy. Insert a new product 7, 'Eraser', Stationery, 10.00, then list all Stationery products. Target: 4 rows: Notebook, Pen, Planner, Eraser
INSERT INTO products (product_id, product_name, category, price) VALUES (7, 'Eraser', 'Stationery', 10.00); SELECT product_id, product_name, price FROM products WHERE category = 'Stationery' ORDER BY product_id;
Easy. Insert customers 7 Gita (Ludhiana, no email) and 8 Harpreet (Ludhiana, harpreet@example.test) in one statement, then list Ludhiana customers. Target: 2 rows: Gita (email NULL), Harpreet
INSERT INTO customers (customer_id, customer_name, city, email) VALUES (7, 'Gita', 'Ludhiana', NULL), (8, 'Harpreet', 'Ludhiana', 'harpreet@example.test'); SELECT customer_id, customer_name, email FROM customers WHERE city = 'Ludhiana' ORDER BY customer_id;
Easy. Inside a transaction, change Pen's price to 25, check it, then ROLLBACK. Target: 25.00 inside; 20.00 after ROLLBACK
START TRANSACTION; UPDATE products SET price = 25 WHERE product_id = 2; SELECT price FROM products WHERE product_id = 2; ROLLBACK;
Easy. Insert a new customer 7 'Gita', Ludhiana, then roll it back. Target: 7 inside; 6 after ROLLBACK
START TRANSACTION; INSERT INTO customers VALUES (7, 'Gita', 'Ludhiana', 'gita@example.test'); SELECT COUNT(*) FROM customers; ROLLBACK;
Medium. Record a new pending order 109 for Farah (customer 6) dated 2026-03-15, then show Farah's orders. Target: 1 row: 109, 2026-03-15, pending
INSERT INTO orders (order_id, customer_id, order_date, status) VALUES (109, 6, '2026-03-15', 'pending'); SELECT order_id, order_date, status FROM orders WHERE customer_id = 6;
Medium. Raise the Mug's price by 10% and show its new price. Target: 1 row: Mug 275.00
UPDATE products SET price = price * 1.10 WHERE product_id = 4; SELECT product_name, price FROM products WHERE product_id = 4;
Medium. Cancel pending order 107, then show how many orders have each status. Target: 2 rows: cancelled 2, completed 6
UPDATE orders SET status = 'cancelled' WHERE order_id = 107 AND status = 'pending'; SELECT status, COUNT(*) AS orders FROM orders GROUP BY status ORDER BY status;
Medium. Change an order status and roll it back. Target: Order 107 is completed inside the transaction and pending afterwards.
START TRANSACTION; UPDATE orders SET status='completed' WHERE order_id=107; SELECT status FROM orders WHERE order_id=107; ROLLBACK; SELECT status FROM orders WHERE order_id=107;
Medium. Give Electronics a 10% price cut inside a transaction, show the new prices, roll back. Target: Desk lamp 720, Keyboard 1080 inside; 800 / 1200 after
START TRANSACTION; UPDATE products SET price = price * 0.9 WHERE category = 'Electronics'; SELECT product_name, price FROM products WHERE category = 'Electronics' ORDER BY product_id; ROLLBACK;
Hard. Delete the order items that belong to cancelled orders (use a subquery), then count the remaining items. Target: 1 row: items_left = 13
DELETE FROM order_items WHERE order_id IN (SELECT order_id FROM orders WHERE status = 'cancelled'); SELECT COUNT(*) AS items_left FROM order_items;
Hard. Give every Stationery product 5% off, rounded to 2 decimals, and list the new Stationery prices. Target: 3 rows: Notebook 95.00, Pen 19.00, Planner 285.00
UPDATE products SET price = ROUND(price * 0.95, 2) WHERE category = 'Stationery'; SELECT product_name, price FROM products WHERE category = 'Stationery' ORDER BY product_id;
Medium. Delete the cancelled order 104 inside a transaction. Why must its items go first? Target: 7 orders inside; 8 after ROLLBACK
START TRANSACTION; DELETE FROM order_items WHERE order_id = 104; DELETE FROM orders WHERE order_id = 104; SELECT COUNT(*) FROM orders; ROLLBACK;
Professional. Remove cancelled order 104 for good: in one transaction delete its items and the order, verify the counts, and COMMIT. Target: 1 row: orders_left = 7, items_left = 13 (and the change is committed)
START TRANSACTION; DELETE FROM order_items WHERE order_id = 104; DELETE FROM orders WHERE order_id = 104; SELECT (SELECT COUNT(*) FROM orders) AS orders_left, (SELECT COUNT(*) FROM order_items) AS items_left; COMMIT;
Professional. Safe price update: preview, START TRANSACTION, UPDATE Mug to 275, verify exactly 1 row changed, ROLLBACK. Target: changed = 1; Mug back to 250.00 after ROLLBACK
SELECT * FROM products WHERE product_id = 4; START TRANSACTION; UPDATE products SET price = 275 WHERE product_id = 4; SELECT ROW_COUNT() AS changed; ROLLBACK;
Up next · Lesson 14
Views, indexes and EXPLAIN
80 min · 15 practice problems