And look, reading from a MySQL database is harmless. Writing to it is where lawyers and auditors start to pay attention. When you INSERT, UPDATE, or DELETE, you’re changing data for every application that uses that database, so it’s worth knowing exactly how to do it—and how to undo it.
This lesson focuses on MySQL DML and transactions.
You will learn to:
- Describe each DML statement:
INSERT,UPDATE,DELETE,TRUNCATE. - Insert new rows (single and multi‑row) safely.
- Update and delete rows, including using subqueries.
- Group changes into transactions with
START TRANSACTION,COMMIT,ROLLBACK, andSAVEPOINT. - Understand how MySQL handles reads and locks with
FOR UPDATE.
1. DML in MySQL and What a Transaction Is
DML (Data Manipulation Language) in MySQL:
INSERT– add rows.UPDATE– change data in existing rows.DELETE– remove rows.
Each of these can participate in a transaction—a series of statements that should succeed or fail as a unit.
Examples of single transactions:
- Move an employee to a "retired" table, then delete them from
employees. - Insert a department, then insert its initial employees.
In a healthy world, you either do all the steps or do none of them.
By default, MySQL runs with autocommit ON, meaning each successful DML statement is committed immediately as its own transaction. You can turn that off or explicitly start a multi‑statement transaction when you want more control.
2. INSERT – Adding New Rows
2.1 Basic multi‑row INSERT with VALUES
MySQL lets you insert one or many rows in a single INSERT:
INSERT INTO departments (department_id, department_name, manager_id, location_id)
VALUES (70, 'Public Relations', 100, 1700),
(150, 'Shareholder Services', NULL, 1700);
Rules:
- Column list after the table name defines target columns.
- Each parentheses group corresponds to one row.
- Values in each group are positional.
- You must have the same number of values as columns listed.
- Character and date values go in single quotes.
2.2 INSERT without column list
You can omit the column list if you:
- Provide all columns.
- Respect the table’s default column order.
INSERT INTO departments
VALUES (160, 'HR Shared Services', 200, 1700),
(170, 'Analytics', 201, 1700);
This works but is fragile—changes to the table structure can break it. Naming columns is safer.
2.3 Inserting NULL explicitly or implicitly
Two ways to get NULL into a column:
- Implicit: leave the column out of the column list.
INSERT INTO departments (department_id, department_name)
VALUES (200, 'Finance West'); -- manager_id and location_id become NULL
- Explicit: use the
NULLkeyword.
INSERT INTO departments (department_id, department_name, manager_id, location_id)
VALUES (210, 'Finance East', NULL, NULL);
Make sure the target columns allow NULL values.
2.4 Inserting dates and times in MySQL
MySQL’s default date format is YYYY-MM-DD. You can use:
CURDATE()– current date.NOW()– current date and time.
INSERT INTO employees (employee_id, last_name, hire_date, salary)
VALUES (300, 'Nguyen', CURDATE(), 5000.00);
If you have a string in a different format, use STR_TO_DATE (or STR_TO_DATE’s cousin STR_TO_DATE—yes, the naming is weird):
INSERT INTO employees (employee_id, last_name, hire_date, salary)
VALUES (301, 'Lee', STR_TO_DATE('Feb 3 2016', '%b %e %Y'), 4500.00);
After insertion, selecting from the table will show the canonical MySQL date format.
2.5 INSERT ... SELECT – Bulk insert from another table
You can insert multiple rows returned by a subquery:
INSERT INTO copy_emp (employee_id, last_name, salary, department_id)
SELECT employee_id,
last_name,
salary,
department_id
FROM employees
WHERE department_id = 80;
Notes:
- No
VALUESclause. - The
INSERTcolumn list must align positionally and by type with theSELECTlist. - All rows returned by the
SELECTare inserted.
3. UPDATE – Changing Existing Data
UPDATE lets you modify existing rows. The danger lies mostly in the WHERE clause.
3.1 Basic UPDATE
UPDATE employees
SET department_id = 50
WHERE employee_id = 113;
- Only the row for employee 113 is affected.
If you omit WHERE:
UPDATE employees
SET department_id = 10;
- Every row in
employeesis updated. - This is how accidental mass updates happen.
3.2 Setting columns to NULL
UPDATE employees
SET manager_id = NULL
WHERE employee_id = 113;
Works if manager_id allows NULL.
3.3 Using subqueries in UPDATE
You can use subqueries in SET, WHERE, or both.
Example – update rows based on another row’s data:
UPDATE copy_emp
SET department_id = (
SELECT department_id
FROM employees
WHERE employee_id = 100
)
WHERE job_id = (
SELECT job_id
FROM employees
WHERE employee_id = 200
);
- The quiz answer from the lesson: you can use subqueries in the SET clause, the WHERE clause, or both.
4. DELETE and TRUNCATE – Removing Rows
4.1 DELETE
DELETE removes rows that match a condition:
DELETE FROM employees
WHERE employee_id = 207;
- Only one row is deleted.
Without a WHERE clause:
DELETE FROM employees;
- All rows are deleted from
employees. - This can be rolled back if you’re inside a transaction and haven’t committed.
You can delete rows based on another table using a subquery:
DELETE FROM employees
WHERE department_id IN (
SELECT department_id
FROM departments
WHERE department_name LIKE 'Public%'
);
This removes all employees working in departments whose names start with Public.
4.2 TRUNCATE TABLE
TRUNCATE wipes all rows from a table, faster than DELETE and more permanently:
TRUNCATE TABLE employees_backup;
- All rows are removed; table structure remains.
- InnoDB treats
TRUNCATEas a DDL operation; it implicitly commits. - You cannot roll back a
TRUNCATE.
Use it when you truly mean, “this table’s data can disappear forever, right now.”
5. Transaction Control in MySQL
By default, MySQL runs with autocommit = 1:
- Every successful
INSERT,UPDATE, andDELETEis committed immediately. - Each statement is its own transaction.
If you want multi‑statement transactions, you either:
- Turn off autocommit:
SET autocommit = 0;, or - Use
START TRANSACTION/BEGINexplicitly.
5.1 START TRANSACTION / BEGIN
START TRANSACTION; -- or BEGIN;
INSERT INTO retired_employees (...)
SELECT ... FROM employees WHERE employee_id = 207;
DELETE FROM employees
WHERE employee_id = 207;
COMMIT;
- If all statements succeed,
COMMITmakes them permanent. - If something fails or you change your mind, use
ROLLBACKinstead ofCOMMITand the changes vanish.
5.2 ROLLBACK
START TRANSACTION;
INSERT INTO departments
VALUES (500, 'Temporary', NULL, NULL);
UPDATE employees
SET department_id = 500
WHERE last_name = 'Smith';
-- Whoops, terrible idea
ROLLBACK;
After ROLLBACK:
- The temporary department and reassignment are undone.
- The database is back to its pre‑transaction state.
5.3 SAVEPOINT
SAVEPOINT lets you set rollback markers inside a transaction.
START TRANSACTION;
INSERT INTO departments
VALUES (600, 'Pilot Dept', NULL, NULL);
SAVEPOINT after_dept;
UPDATE employees
SET department_id = 600
WHERE job_id = 'SA_REP';
-- Decide that reassignment is too aggressive
ROLLBACK TO after_dept;
COMMIT;
- The new department stays (committed).
- The mass reassignment is undone.
- You could also roll back the entire transaction instead of to a savepoint.
Multiple savepoints are allowed; rolling back to an earlier one discards any savepoints created after it.
6. Consistent Reads and Isolation Level
In busy systems, some sessions read while others write.
- Reads do not block writes.
- Writes do not block reads.
- A read sees a snapshot of data.
This snapshot behavior depends on the transaction isolation level. For the MySQL InnoDB storage engine, the default is:
REPEATABLE READ– once a row is read in a transaction, repeated reads see the same values, even if other transactions commit changes.
The practical takeaway:
- You can query without worrying that someone else’s update will change your results mid‑statement.
- Writers still need to worry about conflicts with other writers.
7. Manual Data Locking: SELECT ... FOR UPDATE (MySQL)
In MySQL/InnoDB, you can lock rows explicitly when you intend to update them.
Important:
FOR UPDATEonly makes sense inside a transaction (autocommit off orSTART TRANSACTION).- The lock is released on
COMMITorROLLBACK.
Example:
START TRANSACTION;
SELECT employee_id,
salary
FROM employees
WHERE department_id = 80
FOR UPDATE;
-- Now update the locked rows
UPDATE employees
SET salary = salary * 1.05
WHERE department_id = 80;
COMMIT;
Notes:
FOR UPDATEmust be the last clause in theSELECT(afterWHERE,ORDER BY, etc.).- If another transaction already holds a conflicting lock, your
SELECT ... FOR UPDATEwaits until that lock is released, then returns the rows and acquires the lock.
With joins, you can lock rows from multiple tables:
START TRANSACTION;
SELECT e.employee_id,
e.salary,
d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id
FOR UPDATE;
-- Both employees and departments rows participating in the join may be locked
Or you can restrict which table’s rows are locked (engine‑specific syntax; check your MySQL version’s docs) using FOR UPDATE OF table_name in some SQL dialects. In plain MySQL, you typically control locking via which tables are referenced and how the indexes are used.
8. What You Should Now Be Able to Do
By the end of this lesson, you should be able to:
- Use MySQL
INSERT(with single and multiple value lists) to add rows. - Use
INSERT ... SELECTto bulk‑copy data between tables. - Use
UPDATEandDELETE(with and without subqueries) to change and remove rows. - Use
TRUNCATE TABLEwhen you truly want to empty a table quickly and irreversibly. - Control multi‑statement transactions with
START TRANSACTION/BEGIN,COMMIT,ROLLBACK, andSAVEPOINT. - Understand consistent reads under
REPEATABLE READand useSELECT ... FOR UPDATEwhen you need explicit locks.
In other words, you now know how to change data in MySQL deliberately, not just accidentally—though if you do make a mistake, you’ve at least got the tools to roll it back before anyone notices.