PostgreSQL
Updating Data
Data changes. Customers move cities, products get repriced, orders change status, employees get promotions. The UPDATE statement is how you modify existing rows in PostgreSQL. It sounds straightforward — but UPDATE has several important behaviours, powerful options, and one critical danger that catches many developers off guard. This lesson covers everything you need to update data safely and precisely using the Dataplexa Store dataset.
Basic UPDATE Syntax
An UPDATE statement has three parts — the table to update, the SET clause that specifies which columns to change and what new values to give them, and a WHERE clause that limits which rows are affected. The WHERE clause is not technically required, but leaving it out updates every single row in the table — which is almost never what you want.
-- Update a single customer's city
UPDATE customers
SET city = 'Brooklyn'
WHERE id = 1;
-- Update multiple columns at once — separate with commas
UPDATE employees
SET job_title = 'Senior Account Manager',
salary = 65000.00
WHERE id = 5;
-- Verify the change
SELECT id, job_title, salary FROM employees WHERE id = 5;
UPDATE 1
id | job_title | salary
----+--------------------------+----------
5 | Senior Account Manager | 65000.00
The Critical Rule — Always Use WHERE
This is the most important thing to remember about UPDATE. If you forget the WHERE clause, every single row in the table gets updated. There is no warning, no confirmation prompt — PostgreSQL just does it instantly. This is one of the most common and painful mistakes in SQL development.
-- DANGEROUS — updates EVERY order's status to 'shipped'
UPDATE orders SET status = 'shipped';
-- CORRECT — only updates the specific order
UPDATE orders SET status = 'shipped' WHERE id = 42;
-- Safe practice: run a SELECT with the same WHERE first to preview affected rows
SELECT id, status FROM orders WHERE id = 42;
-- Once confirmed, run the UPDATE
UPDATE orders SET status = 'shipped' WHERE id = 42;
UPDATE 200 -- all 200 orders just changed
-- Safe version:
id | status
----+---------
42 | pending
UPDATE 1
Updating with Expressions and Calculations
The SET clause does not have to use static values — you can use expressions, calculations, and references to the column's own current value. This is how you increment counters, apply discounts, calculate new values based on existing ones, and perform bulk mathematical updates.
-- Give all Electronics products a 10% price reduction
UPDATE products
SET price = price * 0.90
WHERE category = 'Electronics';
-- Increase all Sales department salaries by $5,000
UPDATE employees
SET salary = salary + 5000
WHERE department = 'Sales';
-- Add 20 units to stock for a specific product
UPDATE products
SET stock_qty = stock_qty + 20
WHERE id = 3;
-- Deactivate all products with zero stock
UPDATE products
SET is_active = FALSE
WHERE stock_qty = 0;
SELECT name, price FROM products WHERE category = 'Electronics' LIMIT 3;
UPDATE 6
UPDATE 1
UPDATE 4
name | price
----------------+-------
Wireless Mouse | 26.99
Webcam HD | 71.99
USB Hub | 22.49
UPDATE with RETURNING
Just like INSERT, UPDATE supports the RETURNING clause. This lets you see the new values of the rows you just changed without needing a separate SELECT query. It is particularly useful when you want to confirm what was changed or when your application needs the updated values immediately.
-- Update and immediately see the new values
UPDATE products
SET price = price * 1.05, -- 5% price increase
stock_qty = stock_qty - 1
WHERE id = 7
RETURNING id, name, price, stock_qty;
-- Update an order status and return the updated row
UPDATE orders
SET status = 'delivered'
WHERE id = 15
RETURNING id, customer_id, status, total_amount;
----+-----------+--------+-----------
7 | Desk Lamp | 36.74 | 59
id | customer_id | status | total_amount
----+-------------+-----------+--------------
15 | 8 | delivered | 234.50
UPDATE with Subquery
Sometimes the new value you want to set comes from another table. You can use a subquery inside SET to look up the value dynamically, or use a subquery in the WHERE clause to filter rows based on data in a different table.
-- Update order total based on the sum of its line items
UPDATE orders
SET total_amount = (
SELECT SUM(quantity * unit_price)
FROM order_items
WHERE order_items.order_id = orders.id
)
WHERE status = 'pending';
-- Deactivate products that have never appeared in any order
UPDATE products
SET is_active = FALSE
WHERE id NOT IN (
SELECT DISTINCT product_id FROM order_items
);
SELECT COUNT(*) FROM orders WHERE total_amount IS NOT NULL;
UPDATE 3
count
-------
87
UPDATE with JOIN — Using FROM
PostgreSQL supports updating rows based on a join with another table using the FROM clause in an UPDATE statement. This is a PostgreSQL-specific extension to standard SQL and is often cleaner than using subqueries when the update logic involves multiple tables.
-- Update order status to 'vip' for customers in the vip_customers table
UPDATE orders
SET status = 'vip-pending'
FROM vip_customers
WHERE orders.customer_id = vip_customers.id
AND orders.status = 'pending';
-- Give a 15% raise to employees in the same department as employee id 5
UPDATE employees AS e1
SET salary = e1.salary * 1.15
FROM employees AS e2
WHERE e1.department = e2.department
AND e2.id = 5
AND e1.id != 5; -- don't update the reference employee again
SELECT first_name, salary FROM employees WHERE department = 'Sales' LIMIT 3;
UPDATE 5
first_name | salary
------------+----------
James | 74750.00
Sarah | 57500.00
Marcus | 63250.00
Conditional UPDATE with CASE
Sometimes different rows need different new values depending on their current data. Instead of running multiple UPDATE statements, you can use a CASE expression inside SET to apply different values to different rows in one pass.
-- Assign customer tier based on their total spending
UPDATE customers
SET tier = CASE
WHEN id IN (SELECT customer_id FROM orders GROUP BY customer_id HAVING SUM(total_amount) > 1000) THEN 'gold'
WHEN id IN (SELECT customer_id FROM orders GROUP BY customer_id HAVING SUM(total_amount) > 500) THEN 'silver'
ELSE 'standard'
END;
-- Apply different discounts based on product category
UPDATE products
SET price = CASE
WHEN category = 'Electronics' THEN price * 0.85 -- 15% off electronics
WHEN category = 'Furniture' THEN price * 0.90 -- 10% off furniture
ELSE price * 0.95 -- 5% off everything else
END;
SELECT name, category, price FROM products LIMIT 5;
UPDATE 36
name | category | price
----------------+-------------+-------
Wireless Mouse | Electronics | 22.94
Standing Desk | Furniture | 314.99
Monitor Arm | Accessories | 47.44
Webcam HD | Electronics | 61.19
Desk Lamp | Office | 33.22
UPDATE Quick Reference
| Pattern | Use When |
|---|---|
| UPDATE t SET col = val WHERE ... | Standard update of specific rows |
| SET col = col * 0.9 | Calculate new value from current value |
| UPDATE ... RETURNING | Get updated values back without a second SELECT |
| SET col = (SELECT ...) | New value comes from another table |
| UPDATE ... FROM other_table | Join-based update across tables |
| SET col = CASE WHEN ... END | Different rows need different new values |
🧪 Practice Questions
Answer based on what you learned in this lesson.
1. What happens if you run UPDATE without a WHERE clause?
2. Which clause returns the updated row values immediately after an UPDATE without needing a separate SELECT?
3. Which clause in an UPDATE statement allows you to join another table to use its values during the update?
4. Which SQL expression lets you apply different new values to different rows in a single UPDATE statement?
5. What is the safest practice before running an UPDATE that will affect many rows?
🎯 Quiz — Test Your Understanding
Q1. Which statement correctly applies a 10% discount to all Electronics products?
Q2. Before updating all pending orders for customers in Texas, what should you do first?
Q3. You want to update each order's total_amount to the sum of its line items. Which approach is correct?
Q4. How do you update multiple columns in a single UPDATE statement?
Q5. What is the advantage of using UPDATE … FROM over a subquery in the WHERE clause?
Next up: Deleting rows precisely and safely — the DELETE statement in full.