Q1In INSERT INTO stock(sku, qty) VALUES ('A001', 15) ON CONFLICT(sku) DO UPDATE SET qty = qty + excluded.qty;, with A001 already existing (qty 120), what is the qty after the UPSERT?
UPSERT (ON CONFLICT) and Bulk INSERT Applications
This article is part of the SQL Course, where you master practical SQL skills from scratch, from the fundamentals through to complex queries and SQL tuning.
Use INSERT … ON CONFLICT(sku) DO UPDATE on the stock table to add excluded.qty for the existing A001, insert a fresh A006, protect existing rows with DO NOTHING, and bulk-UPSERT all 3 rows from stock_in — hands-on.
The data we'll use — stock and stock_in
UPSERT (a blend of UPDATE and INSERT —
the operation "update if it exists, insert if it doesn't" in a single statement) is implemented via the INSERT … ON CONFLICT(key) DO UPDATE syntax.
If the row collides with a primary key or UNIQUE constraint, the update runs; if not, it's inserted as-is.
ON CONFLICT DO UPDATE — update if it exists, insert if it doesn't
Writing INSERT INTO table(...) VALUES (...) ON CONFLICT(key_col) DO UPDATE SET col = ... means: if the row you're trying to insert collides with the key declared in ON CONFLICT (the primary key or a UNIQUE column), the DO UPDATE update runs; if there's no collision, the row is simply inserted.
Inside DO UPDATE, a special table name excluded lets you reference "the values of the row you tried to insert."
Write qty = qty + excluded.qty and, on collision, the existing qty gets incremented by the qty you tried to insert.
Use qty = excluded.qty for overwrite and qty = qty + excluded.qty for accumulation, depending on what you need.
-- Collides with existing sku → DO UPDATE adds to qty
INSERT INTO stock(sku, name, qty, price)
VALUES ('A001', 'Pen', 5, 80)
ON CONFLICT(sku) DO UPDATE SET qty = qty + excluded.qty;
-- New sku → no collision, just inserted
INSERT INTO stock(sku, name, qty, price)
VALUES ('A006', 'Marker', 3, 120)
ON CONFLICT(sku) DO UPDATE SET qty = qty + excluded.qty;
SELECT sku, name, qty FROM stock WHERE sku IN ('A001', 'A006');
DO NOTHING — do nothing on collision
When you want neither to update nor to insert — that is, "silently skip on collision" — use ON CONFLICT(key) DO NOTHING.
With no collision the row is inserted; on collision the row is ignored without an error.
In the example below, trying to insert the existing A002 with DO NOTHING collides and is ignored, while the new A007 is inserted.
-- Ignore on collision (DO NOTHING)
INSERT INTO stock(sku, name, qty, price)
VALUES ('A002', 'Note', 999, 999)
ON CONFLICT(sku) DO NOTHING;
-- A002 keeps its original values (qty 60 / price 250)
SELECT sku, name, qty, price FROM stock WHERE sku = 'A002';
Multi-row UPSERT — combine bulk INSERT with ON CONFLICT
When you attach ON CONFLICT to a multi-row INSERT — VALUES (...),(...),(...) separated by commas — each row independently "updates on collision, inserts if not."
You can reflect arrival data in one statement, replacing a procedural loop that branches between UPDATE and INSERT per row with a single SQL.
Even in a multi-row UPSERT, excluded still means "the value you tried to insert for this row," so writing qty = qty + excluded.qty applies "add for existing rows, insert as-is for new rows" on a per-row basis.
This article's final exercise is a bulk UPSERT of all 3 rows of stock_in (A001 / A004 / A006).
-- Multi-row UPSERT: existing rows accumulate qty and refresh price; new rows are inserted
INSERT INTO stock(sku, name, qty, price)
VALUES
('A002', 'Note', 10, 260),
('A005', 'Glue', 20, 190),
('A007', 'Ruler', 15, 90)
ON CONFLICT(sku) DO UPDATE
SET qty = qty + excluded.qty,
price = excluded.price;
SELECT sku, name, qty, price FROM stock ORDER BY sku;
Knowledge Check
Answer each question one by one.
Q2What does excluded refer to inside an UPSERT's DO UPDATE?
Q3Which syntax do you use to never modify existing data and silently ignore collisions without an error?