learn-sql-with-phoebe / Builder session 8 of 10
Learn SQL with Phoebe · Builder track · Session 8 of 10

Building and changing data

For seven sessions you only READ the Daybreak database - SELECT, WHERE, JOIN, GROUP BY, windows. Tonight you get the other half of SQL: you WRITE. You will create a table from scratch, add rows, change them, delete them, and reshape a table's structure. And because every editor on this page runs against a fresh copy of the database, you can INSERT, UPDATE, and DELETE all you like - nothing you do here touches anyone else's data or even the next box on your own screen.

🟠 Builder track Practitioners: analysts · DE · DS · PMs Runs in your browser · SQLite You WRITE data now
0-3 · Read to write 3-18 · CREATE TABLE & keys 18-40 · INSERT · UPDATE · DELETE 40-45 · ALTER & execution order
Part 0

From reading to writing

Everything so far has been read-only: you asked Daybreak questions and it answered. The commands in this session actually change data - they are how the rows got there in the first place. This is where analysts become builders: you can now stand up a table, seed it, correct it, and evolve it. And here is the safety net that makes learning this painless - every Run in this course starts from a clean, freshly seeded copy of the database. Your writes are real while the query runs, then they vanish. No box affects another box, and no learner affects another learner.

Live - presented in session Self-study - read after class ▶ Live SQL - editable & runnable Official sources covered
★ What you walk out with today The four data-definition and data-modification verbs every builder uses daily - CREATE TABLE, INSERT, UPDATE, DELETE - plus ALTER TABLE and the single most clarifying idea in SQL: the query does not run in the order you wrote it. Once you see the real execution order, half of SQL's "why won't this work" mysteries dissolve.
Part 1 · covers SQLBolt L16-17, W3Schools "Create Table / Constraints"

CREATE TABLE, data types, and keys 6 min live

A table is a promise about shape: these columns, these types, these rules. CREATE TABLE writes that promise. Each column gets a type (INTEGER, TEXT, REAL), and you can attach constraints - PRIMARY KEY to guarantee each row is uniquely identifiable, NOT NULL to forbid missing values, FOREIGN KEY to point at another table's key.

CREATE TABLE roasters ( roaster_id INTEGER PRIMARY KEY name TEXT NOT NULL country TEXT ); type = shape of value constraint = the rule PRIMARY KEY: this column uniquely names each row. No two roasters share an id. NOT NULL: a value is required here - the database rejects a blank name. FOREIGN KEY (elsewhere): a column that must match a key in another table - the link. Types and constraints are how a table protects itself from bad data before it ever arrives.
🔍 Click to zoom - the parts of a CREATE TABLE statement
LiveBuild a table from nothing, then fill it3 min

This editor starts from an empty database - no Daybreak tables at all - so you can watch a table come into existence. It creates roasters, inserts two rows, then reads them back. Press ▶ Run, then add a third roaster to the INSERT and run again.

CREATE TABLE roasters (
  roaster_id INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  country TEXT
);
INSERT INTO roasters VALUES (1,'Highland','Kenya'),(2,'Volcanica','Costa Rica');
SELECT * FROM roasters;
Real world

Types catch bugs at the door. A team once stored prices as TEXT to "keep it simple". Sorting by price then ordered them alphabetically - "100" came before "9" - and a discount script silently mis-ranked the catalog for weeks. Declaring price REAL would have made the data wrong-shaped and loud, not quietly wrong.

Self-studyPRIMARY KEY vs FOREIGN KEY, in one breath2 min read

Two keys, two jobs. A PRIMARY KEY answers "which row is this?" - it is unique and never NULL, so it names each record. A FOREIGN KEY answers "which row over there does this point at?" - it holds a value that must exist as a primary key in another table. In Daybreak, customers.customer_id is a primary key; orders.customer_id is the matching foreign key, and that pairing is exactly what every JOIN from b4 rode on.

  • PRIMARY KEY: one per table, unique + not null. The row's identity.
  • FOREIGN KEY: a pointer to another table's key. Keeps relationships honest.
  • Together: they are why you can store a customer's city once and still attach it to 40 orders - the JOIN follows the key.
Part 2 · covers SQLBolt L13-15, W3Schools "Insert / Update / Delete"

INSERT, UPDATE, DELETE 8 min live

Three verbs change rows. INSERT adds them, UPDATE edits values in rows that already exist, DELETE removes rows. Each box below runs on a fresh seeded copy of Daybreak, so you can hit real customer and order data safely - do the write, then a SELECT right after to see the effect, then run again knowing the next run resets everything.

LiveINSERT - add a new customer3 min

Daybreak signs up a test buyer. We add one row to customers, then read it back to confirm it landed. The SELECT filters to just the new id so you see exactly what changed.

INSERT INTO customers VALUES (99,'Test Buyer','Oslo','Norway','2026-06-15','Pro');
SELECT * FROM customers WHERE customer_id = 99;

Values line up with the column order: id, name, city, country, signup_date, plan. Get the order right and every field lands in its place.

LiveUPDATE - a coffee price bump3 min

Every coffee goes up by one. UPDATE touches existing rows; the WHERE decides which ones. Here it is every product in the Coffee category - Equipment and Add-ons are left alone.

UPDATE products SET price = price + 1 WHERE category='Coffee';
SELECT name, price FROM products WHERE category='Coffee';
The most expensive typo in SQL An UPDATE or DELETE with no WHERE hits every row. On this page that just resets next run - in production it is the classic outage story. Habit to build now: write the WHERE first, then the SET.
LiveDELETE - clear out cancelled orders3 min

DELETE removes whole rows. We drop every cancelled order, then count what remains. Because the copy is fresh each run, the original 33 orders are always back on the next Run.

DELETE FROM orders WHERE status='cancelled';
SELECT COUNT(*) AS remaining FROM orders;
Real world

Soft delete beats hard delete. Many teams never truly DELETE customer or order rows - they add a status or is_deleted flag and UPDATE it instead, so history stays auditable and mistakes are reversible. Notice Daybreak already does a version of this: cancelled and refunded orders are kept, just marked - so revenue queries can exclude them without losing the record.

Part 3 · covers W3Schools "Alter Table" + SQLBolt L12 "Order of execution"

ALTER TABLE and the real order of execution 7 min live

ALTER TABLE changes a table's structure after it exists - add a column, rename it, drop it. And then the idea that reorganizes how you read every query you have written: SQL does not run top to bottom the way you type it. The engine runs FROM first and SELECT almost last.

1 FROM 2 WHERE 3 GROUP BY 4 HAVING 5 SELECT 6 ORDER BY 7 LIMIT You WRITE it: SELECT ... FROM ... WHERE ... GROUP BY ... ORDER BY ... LIMIT. The engine RUNS it in the numbered order above - FROM picks the rows, SELECT names columns near the end. This is why a column alias you create in SELECT cannot be used back in WHERE - WHERE ran first. It is also why WHERE filters rows but HAVING filters groups: HAVING runs after GROUP BY.
🔍 Click to zoom - the order the engine actually runs your clauses
LiveALTER a table - add a column, then use it3 min

Starts from an empty database. We make a bare tmp table with one column, then ALTER it to add a label column, insert a row that uses both, and read it back. Watch the table's shape change without dropping and rebuilding it.

CREATE TABLE tmp (id INTEGER);
ALTER TABLE tmp ADD COLUMN label TEXT;
INSERT INTO tmp VALUES (1,'hello');
SELECT * FROM tmp;
UPDATE vs ALTER, the one-liner UPDATE changes the values inside rows. ALTER changes the table's structure - the columns themselves. Editing a customer's city is UPDATE; adding a whole "loyalty_tier" column is ALTER.
Self-studyWhy execution order saves you from bugs2 min read

Two classic errors both come from forgetting the real order. First: you alias SUM(quantity*unit_price) AS revenue in SELECT, then try WHERE revenue > 100 - and it fails, because WHERE ran before SELECT existed. The fix is HAVING revenue > 100, which runs after grouping. Second: you cannot filter on a window-function result in WHERE for the same reason - windows compute at SELECT time. Once the numbered pipeline is in your head, these stop being surprises and start being predictable.

  • Filter raw rows? WHERE - it runs early, before grouping.
  • Filter groups or aggregates? HAVING - it runs after GROUP BY.
  • Rename for output? Aliases in SELECT are for ORDER BY and the result, not for WHERE.
Build-along

Stand up a mini table and evolve it ★ 10 min · everyone builds

Put all four verbs together on a fresh, empty database. Create a small promos table, seed it, correct a value, remove a stale row, then reshape it - the full lifecycle a builder runs every week.

CREATE TABLE promos (code TEXT PRIMARY KEY, pct INTEGER); - a promo code and its discount percent.

INSERT INTO promos VALUES ('SPRING',10),('WELCOME',15); - two live promos.

UPDATE promos SET pct = 20 WHERE code='WELCOME'; - bump the welcome offer.

ALTER TABLE promos ADD COLUMN active TEXT; then read it back - the shape evolved.

CREATE TABLE promos (code TEXT PRIMARY KEY, pct INTEGER);
INSERT INTO promos VALUES ('SPRING',10),('WELCOME',15);
UPDATE promos SET pct = 20 WHERE code='WELCOME';
ALTER TABLE promos ADD COLUMN active TEXT;
SELECT * FROM promos;
Real world

This is a migration. When engineers "ship a migration", this is literally what runs: a scripted sequence of CREATE / ALTER / UPDATE against the real database, version-controlled and reviewed. The verbs you just used are the exact same ones powering the schema changes behind every product you use - the difference is only scale and a review step.

Homework

Try it yourself - this week ◐ 20-30 min total

Source material

Official sources covered

This session teaches the data-definition and data-modification core of the major free SQL curricula, run on live data instead of screenshots. Graded problem sets stay on the official sites. This page covers:

SQLBolt Lessons 13-18 - Insert / Update / Delete, Create / Alter / Drop TableParts 1-3 · the full write side, each verb run live
W3Schools - SQL Database (Create / Alter / Drop, Constraints, PRIMARY / FOREIGN KEY)Part 1 · types and key constraints, W3Schools reference stays linked
SQLBolt Lesson 12 - Order of executionPart 3 · the logical clause order, with the alias-in-WHERE gotcha
Check yourself

Three questions before you go 🎯 ◐ 90 seconds

1 · You run an INSERT in one editor on this page. What happens to the next editor?

Each Run executes against a fresh seeded copy. Your INSERT / UPDATE / DELETE is real for that run, then gone - no box or learner affects another.

2 · What is the difference between UPDATE and ALTER?

UPDATE edits data in existing rows. ALTER reshapes the table itself - adding, renaming, or dropping columns. Editing a city is UPDATE; adding a whole column is ALTER.

3 · Does SQL run top-to-bottom in the order you wrote the clauses?

The engine runs FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT. Because SELECT runs after WHERE, an alias you make in SELECT does not exist yet when WHERE runs.

Builder session 8 cheat sheet · pin this

CREATE TABLEDefines a table: columns, types (INTEGER / TEXT / REAL), and constraints. The shape promise.
PRIMARY vs FOREIGN KEYPRIMARY names each row (unique, not null). FOREIGN points at another table's key - the JOIN link.
INSERTAdds rows. Values match column order: INSERT INTO customers VALUES (99,'Test Buyer',...).
UPDATE ... WHEREEdits values in existing rows. Always write the WHERE - no WHERE hits every row.
DELETE ... WHERERemoves whole rows. Same WHERE discipline. Teams often soft-delete with a flag instead.
ALTER TABLEChanges structure: ADD / RENAME / DROP COLUMN. Reshapes the table without rebuilding it.
Execution orderFROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT. Not the written order.
Fresh-copy safetyEvery Run reseeds the database. Write freely - it resets, never touching other boxes or learners.