Q1If you run UPDATE stock SET qty = qty + (SELECT SUM(add_qty) FROM stock_in WHERE stock_in.sku = stock.sku); without a WHERE EXISTS (...), what happens to qty for skus that have no matching row in stock_in?
UPDATE / DELETE with Subqueries and JOINs
Using a stock table and a stock_in arrivals table, learn correlated-subquery bulk updates, UPDATE … FROM joins, and DELETE with subquery conditions, plus how to check the affected row count — all hands-on in your browser.
The data we'll use — stock and stock_in
This article covers a more advanced style of writing — updating and deleting using values from another table or an aggregate result.
Specifically, three patterns: updates whose values are computed by a correlated subquery (a subquery whose inner SELECT references columns from the outer row, so it's evaluated per row), join-style updates via UPDATE … FROM against another table, and DELETE statements with a subquery in the WHERE clause.
Use a correlated subquery to update with values from another table
Writing UPDATE table SET col = (SELECT ... WHERE subquery.key = table.key) evaluates the inner SELECT once per target row and writes the result into the column.
Because the inner SELECT references a column from the outer UPDATE target (stock.sku), it's called a correlated subquery.
The gotcha is this: for stock rows that have no matching row in stock_in, the inner SELECT returns NULL.
-- Without COALESCE: qty is overwritten with NULL for skus that have no match
UPDATE stock
SET qty = qty + (SELECT SUM(add_qty) FROM stock_in WHERE stock_in.sku = stock.sku);
If you run qty = qty + (SELECT ...) as-is, rows without an arrival get their qty overwritten with NULL.
To prevent this, either wrap the subquery in COALESCE(subquery, 0) to turn NULL into 0, or narrow the target with WHERE EXISTS (...) so you only touch rows that actually have an arrival.
-- First preview the post-update values with SELECT (stock isn't changed)
SELECT sku, qty,
qty + (SELECT COALESCE(SUM(add_qty), 0) FROM stock_in WHERE stock_in.sku = stock.sku) AS new_qty
FROM stock;
-- Once you're happy, switch to UPDATE (COALESCE converts NULL to 0)
UPDATE stock
SET qty = qty + (SELECT COALESCE(SUM(add_qty), 0) FROM stock_in WHERE stock_in.sku = stock.sku);
UPDATE … FROM: join another table and update
Writing UPDATE table SET col = value FROM other_table WHERE join_condition lets you join the UPDATE target with the table named in FROM and update in one shot, referencing the join side's columns directly in SET.
-- UPDATE … FROM example: bump price by 10 only for items that have an arrival
UPDATE stock
SET price = price + 10
FROM stock_in
WHERE stock.sku = stock_in.sku;
-- Only the skus that joined (A001 / A004) see their price go up
SELECT sku, price FROM stock ORDER BY sku;
Rows in stock that have no matching row on the FROM side (stock_in) fail the join condition in WHERE and drop out of the update target automatically.
Unlike the correlated-subquery form, you don't need to guard against NULL separately.
DELETE with a subquery condition, and checking affected row count
You can put a subquery in the WHERE of DELETE, like DELETE FROM table WHERE col IN (SELECT ...) or WHERE EXISTS (SELECT ...).
This enables deletions that can't be expressed with a fixed value: "delete only rows that exist / don't exist in another table," or "delete rows whose aggregate result meets a condition."
Deletion is destructive and irreversible, so the standard practice in production is to first run the same WHERE inside a SELECT COUNT(*) to check the affected row count, then rewrite to DELETE.
After executing, verify with SELECT COUNT(*) FROM table; to confirm the remaining row count matches what you expected.
-- First check what would be deleted: items below the average price (stock isn't changed)
SELECT sku, name, price FROM stock
WHERE price < (SELECT AVG(price) FROM stock);
-- Once you're happy, switch to DELETE and check the remaining count
DELETE FROM stock
WHERE price < (SELECT AVG(price) FROM stock);
SELECT COUNT(*) AS remaining FROM stock;
Knowledge Check
Answer each question one by one.
Q2In UPDATE stock SET qty = stock.qty + stock_in.add_qty FROM stock_in WHERE stock.sku = stock_in.sku;, how are stock rows that have no match in stock_in handled?
Q3Which is the most appropriate procedure for running a subquery-driven DELETE safely in production?