Changing data: INSERT, UPDATE, DELETE

INSERT, UPDATE, and DELETE change stored data. Along with their syntax, this lesson covers checking the affected rows and rolling back a change when needed.

This lesson changes data. It changes your browser copy of the sample database. Reload the sample from the welcome screen to start over.

INSERT

Name the columns, then give a value for each. The columns you leave out get their default — added_at here defaults to the current timestamp.

1

INSERT INTO wishlists (customer_id, product_id)
VALUES (1, 288);
Run in Planche

1 row changed

Opens Planche in a separate browser tab, with the sample database loaded and this query ready. Every example on the page reuses that tab, and nothing runs until you press F9.

One row was inserted. Naming columns explicitly avoids relying on the table's column order, so the statement remains valid when columns are added later.

What the database refuses

Run exactly the same statement again.

2

-- The same statement a second time. (customer_id, product_id) is the
-- primary key of wishlists, so the database refuses the duplicate.
INSERT INTO wishlists (customer_id, product_id)
VALUES (1, 288);
Run in Planche

Error

UNIQUE constraint failed: wishlists.customer_id, wishlists.product_id

The statement raises an error and changes nothing. wishlists has PRIMARY KEY (customer_id, product_id), so the same pair cannot exist twice. The constraint prevents invalid data from being stored.

UPDATE, and the clause that matters

3

UPDATE customers
SET loyalty_points = loyalty_points + 100
WHERE id = 1;
Run in Planche

1 row changed

Before changing data, run a SELECT with the same FROM and WHERE clauses to inspect the affected rows.

Planche can do this automatically. With Settings › Safety › Preview affected rows enabled, an UPDATE or DELETE is rewritten as a matching SELECT, counted, and shown before it runs. Read-only connections and per-connection production colors are configured in the same place.

4

SELECT id, first_name, last_name, loyalty_points
FROM customers
WHERE id = 1;
Run in Planche

Result — 1 row

idfirst_namelast_nameloyalty_points
1LiamFischer3619

DELETE

5

DELETE FROM wishlists
WHERE customer_id = 1
  AND product_id = 288;
Run in Planche

1 row changed

Choosing the rows with a subquery

The WHERE clause of an UPDATE or DELETE is an ordinary WHERE clause, so everything from the subqueries lesson works here. A subquery is how you change rows in one table based on what is in another.

6

-- Which rows to change is its own question, and a subquery can answer it.
UPDATE customers
SET loyalty_points = loyalty_points + 50
WHERE id IN (
  SELECT customer_id
  FROM orders
  WHERE status = 'refunded'
    AND total > 2000
);
Run in Planche

242 rows changed

242 customers have a refunded order over 2,000, so 242 rows change. As before, run SELECT id FROM customers WHERE id IN (...) first and check the count before applying the UPDATE.

7

DELETE FROM wishlists
WHERE product_id IN (
  SELECT id FROM products WHERE stock_quantity = 0
);
Run in Planche

260 rows changed

NOT EXISTS and EXISTS work here too, and are the safer choice for the negated case for the reason the subqueries lesson gives. What is not portable is joining directly in an UPDATE or DELETE — every engine spells that differently, and one of them cannot do it at all. The dialect page on it covers all five.

Transactions

A transaction groups statements so they all succeed or all roll back. Until you commit, you can inspect the result and undo the change.

8

BEGIN;

UPDATE coupons
SET used_count = used_count + 1
WHERE code = 'WELCOME10';

SELECT code, used_count FROM coupons WHERE code = 'WELCOME10';

ROLLBACK;
Run in Planche

Result — 1 row

codeused_count
WELCOME1063

BEGIN starts the transaction, the SELECT shows the incremented coupon count, and ROLLBACK restores it. Replacing ROLLBACK with COMMIT makes the change permanent.

9

SELECT code, used_count FROM coupons WHERE code = 'WELCOME10';
Run in Planche

Result — 1 row

codeused_count
WELCOME1062

For a risky change on a real database, open a transaction, run the statement, verify the affected row count, then commit. In Planche, Connection › Manual transactions keeps a transaction open and adds Commit and Rollback controls to the toolbar.

What you can do now

Everything except find out why a query is slow — which is the last lesson.

Try it yourself

  1. Take every product that is out of stock off sale. Check how many rows you expect to change before you run it.

    Show the answer

    Show the answer

    UPDATE products
    SET is_active = 0
    WHERE stock_quantity = 0;
    Run in Planche

    40 rows changed

    40 rows. Running SELECT COUNT(*) FROM products WHERE stock_quantity = 0 first is the whole habit: you know the number before the statement, so an unexpected count is a warning rather than a discovery.

  2. Delete notifications that have been read and are older than 2025.

    Show the answer

    Show the answer

    DELETE FROM notifications
    WHERE is_read = 1
      AND created_at < '2025-01-01';
    Run in Planche

    1202 rows changed

    1,202 rows. Both conditions matter — dropping is_read = 1 would take unread notifications too, and this is a DELETE, so there is nothing to undo unless you opened a transaction first.