Oracle Database
Updating Data
The UPDATE statement modifies existing rows in a table. It can change a single column in a single row, or update dozens of columns across millions of rows in one statement. Without a WHERE clause every row in the table is updated — a mistake that is easy to make and expensive to recover from. This lesson covers single and multi-column updates, subquery-based updates that derive values from other tables, conditional updates using CASE, and how Oracle's transaction model protects you until you commit.
To follow along with the examples in this lesson, use the course dataset from Lesson 8.
Single and Multi-Column UPDATE
The basic UPDATE syntax names the table, lists column assignments in a SET clause, and narrows the affected rows with WHERE. Multiple columns are assigned in a single SET clause separated by commas — there is no need for a second SET keyword. All assignments in the same SET clause are based on the row's values before the update begins, so the order of assignments does not matter.
-- Single column update — correct the loyalty tier for one customer
UPDATE customers
SET loyalty_tier = 'gold'
WHERE customer_id = 4;
-- Verify the change
SELECT customer_id, full_name, loyalty_tier
FROM customers
WHERE customer_id = 4;
-- Multi-column update — update price and stock for a product in one statement
UPDATE products
SET unit_price = 134.99,
stock_qty = stock_qty + 50
WHERE product_id = 3;
-- Verify
SELECT product_name, unit_price, stock_qty
FROM products
WHERE product_id = 3;
-- Update using an expression — apply a 10% price increase to all Fitness products
UPDATE products
SET unit_price = ROUND(unit_price * 1.10, 2)
WHERE category = 'Fitness';
-- Update a DATE column
UPDATE employees
SET hire_date = DATE '2023-06-15'
WHERE employee_id = 105;CUSTOMER_ID FULL_NAME LOYALTY_TIER
─────────── ─────────── ────────────
4 Yuki Tanaka gold
-- Multi-column update: 1 row updated.
PRODUCT_NAME UNIT_PRICE STOCK_QTY
──────────────────────────────────── ────────── ─────────
Wireless Noise-Cancelling Headph… 134.99 120
-- Fitness price increase: 2 rows updated.
-- hire_date update: 1 row updated.
- All column assignments in the same
SETclause read the original row values —SET a = b, b = acorrectly swaps the two values because both read the pre-update state - Always verify the row count in the feedback message —
1 row updatedconfirms theWHEREclause matched exactly one row;0 rows updatedmeans the filter matched nothing and is often a sign of a condition error - An
UPDATEwithout aWHEREclause updates every row in the table — Oracle does not warn you; double-check theWHEREclause before executing any update in a production environment
Subquery-Based UPDATE
When the new value for a column must be derived from another table, a subquery in the SET clause fetches it. The subquery must return exactly one row and one column for each column being updated — returning more than one row raises ORA-01427. If the subquery returns no rows the column is set to NULL, so always verify the subquery returns a result before running the update.
A correlated subquery in the WHERE clause can also restrict which rows are updated based on related data in another table — without needing a join in the UPDATE itself.
-- Update total_amount on each order to reflect the sum of its order items
-- The subquery is correlated — it references the outer order's order_id
UPDATE orders o
SET total_amount = (
SELECT SUM(quantity * unit_price)
FROM order_items oi
WHERE oi.order_id = o.order_id
);
-- Verify a sample
SELECT order_id, total_amount
FROM orders
ORDER BY order_id
FETCH FIRST 5 ROWS ONLY;
-- Update employee salaries to match the average for their department
UPDATE salaries s
SET amount = (
SELECT ROUND(AVG(s2.amount), 2)
FROM salaries s2
JOIN employees e2 ON e2.employee_id = s2.employee_id
WHERE e2.department_id = (
SELECT department_id
FROM employees e3
WHERE e3.employee_id = s.employee_id
)
)
WHERE EXISTS (
SELECT 1
FROM employees e
WHERE e.employee_id = s.employee_id
AND e.department_id = 20
);ORDER_ID TOTAL_AMOUNT
──────── ────────────
5001 1249.95
5002 599.98
5003 899.97
5004 449.98
5005 149.99
-- Department average salary update: 3 rows updated.
- If the subquery in
SETreturns no rows the column is set toNULL— forNOT NULLcolumns this raisesORA-01407; always add aWHERE EXISTSclause or aNVLguard if the subquery might return nothing - The correlated subquery re-executes once for every row that matches the
UPDATE'sWHEREclause — on large tables this can be slow; consider using aMERGEstatement for better performance on bulk updates - Test subquery-based updates by running the subquery as a standalone
SELECTfirst — confirm it returns the expected single value for a sample of rows before attaching it to theUPDATE
Conditional UPDATE with CASE
A CASE expression inside the SET clause applies different values to different rows in a single UPDATE statement. Without CASE you would need separate UPDATE statements for each condition — one statement with CASE is faster because it scans the table once and applies all the logic in a single pass.
-- Reclassify loyalty tiers based on total spend in a single UPDATE
UPDATE customers c
SET loyalty_tier = (
CASE
WHEN (SELECT NVL(SUM(total_amount), 0)
FROM orders o
WHERE o.customer_id = c.customer_id) > 1000 THEN 'platinum'
WHEN (SELECT NVL(SUM(total_amount), 0)
FROM orders o
WHERE o.customer_id = c.customer_id) > 500 THEN 'gold'
ELSE 'standard'
END
);
-- CASE update across multiple columns — adjust price and active flag by stock level
UPDATE products
SET active_flag = CASE
WHEN stock_qty = 0 THEN 'N'
ELSE 'Y'
END,
unit_price = CASE
WHEN stock_qty = 0 THEN ROUND(unit_price * 0.80, 2)
WHEN stock_qty < 20 THEN ROUND(unit_price * 0.90, 2)
ELSE unit_price
END;
-- Verify the tier reclassification
SELECT customer_id, full_name, loyalty_tier
FROM customers
ORDER BY loyalty_tier, customer_id;CUSTOMER_ID FULL_NAME LOYALTY_TIER
─────────── ─────────────── ────────────
1 Marcus Webb gold
5 Elena Vasquez gold
8 Lena Hoffmann gold
2 James Okafor platinum
3 Sarah Mitchell platinum
4 Yuki Tanaka standard
6 Priya Sharma standard
7 Daniel Kim standard
-- Stock-based price and flag update: 15 rows updated.
- A single
UPDATEwithCASEis more efficient than multiple targetedUPDATEstatements because the table is scanned once — each additionalUPDATEstatement causes another full or partial scan - When not all rows are covered by a
CASEbranch, always include anELSEclause — without it, unmatched rows are set toNULL, which silently corrupts data - Multiple columns can each have their own independent
CASEexpressions in the sameSETclause — each column is evaluated separately across all rows in the same single pass
UPDATE and Oracle Transactions
In Oracle, every UPDATE is part of a transaction. Changes are not visible to other sessions until COMMIT is issued, and they can be fully reversed with ROLLBACK at any point before the commit. This is fundamentally different from databases that auto-commit each statement — in Oracle you are always in an explicit transaction.
SAVEPOINT lets you mark a point within a transaction and roll back to it without undoing earlier changes. This is useful in scripts that perform a sequence of updates where you want to reverse only the last step if something goes wrong.
-- First update in the transaction
UPDATE products
SET unit_price = 599.99
WHERE product_id = 1;
-- Mark a savepoint after the first update
SAVEPOINT after_price_update;
-- Second update
UPDATE products
SET stock_qty = 0
WHERE product_id = 1;
-- Something went wrong — roll back to the savepoint
-- The price update is preserved; the stock update is reversed
ROLLBACK TO SAVEPOINT after_price_update;
-- Verify: price changed, stock unchanged
SELECT product_id, unit_price, stock_qty
FROM products
WHERE product_id = 1;
-- Commit the price change permanently
COMMIT;-- Savepoint created.
-- Stock update: 1 row updated.
-- Rollback to savepoint.
PRODUCT_ID UNIT_PRICE STOCK_QTY
────────── ────────── ─────────
1 599.99 50
-- Commit complete.
- Oracle does not auto-commit DML —
INSERT,UPDATE, andDELETEare all pending untilCOMMITis explicitly issued; closing a connection without committing causes an automaticROLLBACK - Other sessions cannot see your uncommitted changes — Oracle's read consistency model means they see the last committed state of the data, not your in-progress transaction
SAVEPOINTnames are session-scoped and reusable — creating a savepoint with the same name as an existing one replaces it; the older savepoint is lost
Preventing Accidental Full-Table Updates
The most dangerous UPDATE mistake is omitting the WHERE clause. Oracle provides no automatic safeguard — it will update every row without hesitation. Several safe practices reduce this risk in day-to-day work.
-- Safe practice 1: run the equivalent SELECT first to preview affected rows
SELECT product_id, product_name, unit_price
FROM products
WHERE category = 'Electronics'
AND unit_price > 100;
-- If the SELECT returns the expected rows, run the UPDATE with confidence
UPDATE products
SET unit_price = ROUND(unit_price * 0.95, 2)
WHERE category = 'Electronics'
AND unit_price > 100;
-- Safe practice 2: check SQL%ROWCOUNT in PL/SQL before committing
DECLARE
v_rows NUMBER;
BEGIN
UPDATE customers
SET loyalty_tier = 'standard'
WHERE country = 'USA';
v_rows := SQL%ROWCOUNT;
IF v_rows > 5 THEN
ROLLBACK;
DBMS_OUTPUT.PUT_LINE('Unexpected row count: ' || v_rows || ' — rolled back.');
ELSE
COMMIT;
DBMS_OUTPUT.PUT_LINE('Updated ' || v_rows || ' rows — committed.');
END IF;
END;
/PRODUCT_ID PRODUCT_NAME UNIT_PRICE
────────── ────────────────────────────────── ──────────
3 Wireless Noise-Cancelling Headph… 134.99
11 Mechanical Keyboard 89.99
12 USB-C Hub 7-Port 39.99
-- 5% discount update: 3 rows updated.
-- PL/SQL block output:
Updated 3 rows — committed.
SQL%ROWCOUNTin PL/SQL returns the number of rows affected by the most recent DML statement — use it as a guard to catch unexpectedly large updates before committing- Running the equivalent
SELECTbefore anUPDATEis the single most effective habit for avoiding accidental full-table updates — it costs nothing and makes the affected rows visible before any change is made - In SQL Developer, changes are not permanent until you click Commit — executing the statement and committing are separate actions, giving you a window to review and roll back if needed
Summary
| Technique | Syntax | Key Point |
|---|---|---|
| Single column UPDATE | UPDATE t SET col = val WHERE ... | Always include WHERE — omitting it updates every row |
| Multi-column UPDATE | SET col1 = v1, col2 = v2 | All assignments read the pre-update row — order does not matter |
| Subquery in SET | SET col = (SELECT ... WHERE correlated) | Must return exactly one row — no rows sets column to NULL |
| CASE in SET | SET col = CASE WHEN ... THEN ... END | One table scan for all conditions — faster than multiple UPDATEs |
| COMMIT | COMMIT | Makes changes permanent and visible to other sessions |
| ROLLBACK | ROLLBACK | Reverses all uncommitted changes in the current transaction |
| SAVEPOINT | SAVEPOINT name / ROLLBACK TO SAVEPOINT name | Partial rollback — reverses only changes made after the savepoint |
| SQL%ROWCOUNT | v := SQL%ROWCOUNT (PL/SQL) | Rows affected by the last DML — use as a guard before committing |
Practice Questions
Practice 1. In a multi-column UPDATE, SET a = b, b = a — does this correctly swap the two values? Why?
Practice 2. A subquery in the SET clause returns no rows for some updated rows. What happens to those columns?
Practice 3. What is the difference between ROLLBACK and ROLLBACK TO SAVEPOINT?
Practice 4. Write an UPDATE that gives all employees in department 30 a 15% salary increase in the salaries table.
Practice 5. Why is running an equivalent SELECT before an UPDATE one of the safest habits in production work?
Quiz
Quiz 1. An UPDATE statement has no WHERE clause. How many rows are affected?
Quiz 2. A subquery in a SET clause returns more than one row. What error does Oracle raise?
Quiz 3. After issuing ROLLBACK TO SAVEPOINT sp1, what is the state of changes made before sp1 was created?
Quiz 4. Why is a single UPDATE with a CASE expression faster than multiple separate UPDATE statements for the same task?
Quiz 5. What does SQL%ROWCOUNT return in a PL/SQL block immediately after an UPDATE?
Next up — Deleting Data — Learn how to remove rows safely using DELETE with WHERE filters, TRUNCATE for fast full-table clears, and how foreign key constraints affect deletion order.