field guide❋ Page 1 of 16 palette size
SQL · Garden
where new beds are designed before they are dug
❋
— Chapter —
III

The Greenhouse

in which the gardener stands at a workbench
drafting the trait card for a new bed of flowers
❋
Spring · Greenhouse

Page 2 · Welcome

Beds don't just exist

someone designed them, then dug them

One quick word, before this chapter begins. Throughout these lessons we keep saying "the bed." A bed is the garden's word for what SQL calls a table — a named, rectangular collection of rows. Each row in the bed is one flower; each column is one trait every flower carries (id, species, color, height). Different beds hold different things — a rose bed holds roses, a herb bed would hold herbs. The bed we've been working in is named rose_bed, and it looks like this in the garden and on the page:

rose_bed the bed, out in the garden 1 2 3 4 5 one rose ↓ the same five roses, written down ↓ rose_bed the table, in SQL id species color height 1 Old English pink 18 2 Crimson Glory purple 22 3 Madame Hardy pink 24 4 Iceberg white 16 5 Carefree Beauty pink 20 ← one row ↑ one column
Same five roses, two views: a real bed of flowers and the SQL table that describes them. A row is one flower; a column is one trait collected across every flower.

The rose bed is just one of many beds the gardener tends. Every bed has its own shape — what it holds, and which traits the gardener writes down for each item in it. A rose bed cares about color and height; a herb bed cares about scent; a vegetable bed cares about how many days until harvest. Same idea — a named rectangular collection of rows — different shape:

rose_bed roses columns id · species color · height 5 rows herb_bed herbs columns id · name scent · perennial 3 rows veg_bed vegetables columns id · kind days_to_harvest 3 rows three beds in the same garden — same idea, different shapes rose_bed (id, species, color, height) · herb_bed (id, name, scent, perennial) · veg_bed (id, kind, days_to_harvest)
Three beds, three shapes. The same garden holds many tables, each with its own columns matching what lives in it. (This chapter will mostly work with the rose_bed; you'll draft a herb_bed of your own at the end, and meet a few others on later pages.)

For two chapters the rose_bed has been a given — five flowers, four colors, columns already there. You walked it with SELECT, then you planted, pruned, and pulled flowers from it. But you never asked: where did the bed come from?

Behind the back of the garden is a glass-roofed workshop. Inside, the gardener stands at a wooden workbench with a small card in her hand — a trait card. Before any rose is planted, the card is filled out: this bed will hold flowers, each with an id, a species, a color, a height. The id is the unique tag — no two flowers share it. The species and color must always be filled in. The height must be a positive number.

That card is the schema. SQL's verb for drafting one is CREATE TABLE. This chapter is about it — and about the rules (constraints) that the trait card can stamp onto the bed.

The greenhouse: where the trait card is drafted before any pot is filled.

Page 3 · The trait card

A schema, named and stamped

the trait card before any flower is planted

A schema (a trait card) has three parts:

  • The name of the bed — what to call this table
  • The columns — what trait every flower will have on its tag
  • The constraints — rules the bed enforces on every flower planted in it

The smallest possible trait card is just a name and a list of columns with their types. Add nothing more, and the bed accepts almost anything. Add constraints, and the bed begins to refuse flowers that don't measure up.

rose_bed (a complete trait card)
CREATE TABLE rose_bed (
  id      INTEGER PRIMARY KEY,
  species TEXT    NOT NULL,
  color   TEXT    NOT NULL DEFAULT 'pink',
  height  INTEGER CHECK (height > 0)
);
Four columns. Four constraints. One bed that won't accept a flower with no species, two flowers with the same id, or a negative height.

Don't worry about the details yet — we'll meet each constraint in its own page. Just notice the shape: CREATE TABLE name (...); with each column on its own line inside the parentheses, name first, type second, constraints after.

Page 4 · CREATE TABLE

Draft the simplest bed

name, columns, types — that's enough to start

The greenhouse starts empty. Press Run query to create the bed and watch the trait card appear on the right. Then try the chip below to plant a test flower with INSERT — without constraints yet, anything goes.

greenhouse.sql
the workbench
CREATE TABLE
Run the query to draft the trait card.
Field guide: CREATE TABLE is followed by the table name, then a parenthesized list of columns. Each column is name type. Commas separate them. A semicolon ends the statement.

Page 5 · Column types

What kind of value goes here?

INTEGER, TEXT, REAL — the three you'll meet most

Every column has a type — a declaration of what kind of value should live there. How strictly that declaration is enforced varies by database: most databases reject a text value placed in an INTEGER column outright, while SQLite (and this lesson's engine) are relaxed and will accept it. Either way, the type still documents your intent — and on the day you move the bed to a stricter database, that documentation becomes enforcement. Three types carry most of the work:

  • INTEGER — whole numbers (1, 2, -3, 200)
  • TEXT — words and sentences, in single quotes ('Old English')
  • REAL — numbers with a decimal point (1.5, 19.25)

SQL has a few more (BOOLEAN, DATE, TIME, BLOB, NUMERIC) and dialects add their own, but these three cover almost everything in our chapters. Pick the type that matches the shape of the value — a height in cm is INTEGER, an average is REAL, a species name is TEXT.

greenhouse.sql
the workbench
types
Five columns, three different types.
Field guide: A quick rule for the values you'll write later in INSERT: numbers go without quotes, text goes in single quotes. 18 is an integer; '18' is the two-character string "18". The bed treats them differently — and a strict database will reject the wrong one outright.

Page 6 · NOT NULL

Required vs optional

NOT NULL means "this can't be left blank"

By default, every column is optional — leave it empty (or pass NULL) and SQL is fine. Stamp NOT NULL on a column and that changes. The bed will refuse a flower that has no value there.

Run the CREATE TABLE below, then click the chip to try planting a rose with no species — you'll see a friendly rejection.

greenhouse.sql
the workbench
NOT NULL
Species and color are now required.
When to use it: any column that logically must have a value. A rose without a species is unidentified; a rose without a color is hard to picture. id, name, species — these are almost always NOT NULL. height could plausibly be unknown for a moment.

Page 7 · PRIMARY KEY

The unique tag on every flower

PRIMARY KEY = NOT NULL + UNIQUE

Every flower in the bed needs to be tellable apart from every other. The PRIMARY KEY is the column that does that — its values must be unique across the whole bed, and it can't be left blank. Usually it's an integer called id.

Run the CREATE TABLE and try inserting two roses with the same id. The second one will bounce off — the bed already has that tag.

greenhouse.sql
the workbench
PRIMARY KEY
id is now the unique tag.
Field guide: a PRIMARY KEY is implicitly NOT NULL and implicitly UNIQUE. You don't need to write NOT NULL alongside it. Most tables have exactly one — usually id INTEGER PRIMARY KEY.

Page 8 · UNIQUE

No two flowers with the same name

UNIQUE refuses a value that's already in the column

Some columns hold values that should never repeat in a bed — a species name, a label, a registration number. The UNIQUE constraint tells the bed: refuse any new value that's already in this column.

Below, species is marked UNIQUE. Try planting two roses with the same species name; the second one will bounce off.

greenhouse.sql
the workbench
UNIQUE
species must be unique across the bed.
Field guide: UNIQUE alone still allows NULL — a column can be UNIQUE but optional, and several rows are allowed to leave it blank. Only PRIMARY KEY closes both doors at once (one row per value, every row must have one). A table can have several UNIQUE columns (a species, a code, a registration) in addition to its primary key.

Page 9 · DEFAULT

The standard value

DEFAULT fills in when you don't

A column with a DEFAULT has a fallback. Insert a flower without specifying that column, and SQL fills it in for you.

Below, color defaults to 'pink' and height defaults to 20. Try the chip to plant a rose with just id and species — the rest will be filled in.

greenhouse.sql
the workbench
DEFAULT
Two columns now have defaults.
Field guide: DEFAULT only applies when the column is not mentioned in the INSERT. If you explicitly pass NULL, that's NULL — different from "missing." Defaults are useful for timestamps, settings, and "almost always this" values.

Page 10 · CHECK

Rules the bed enforces

CHECK refuses values that don't fit

A CHECK constraint is a rule the bed enforces on every attempted plant. The condition uses the same WHERE-style operators you already know.

Below, height must be positive and at most 300 cm; color must be one of four allowed values. Try planting a rose with a wild value to see the bed turn it away.

greenhouse.sql
the workbench
CHECK
Two CHECK rules now patrol the bed.
Field guide: CHECK runs on every INSERT and UPDATE. If the condition is false, the change is refused — the bed stays as it was. Common patterns: height > 0, color IN ('a','b','c'), name <> ''.

Page 11 · FOREIGN KEY

A label pointing to another bed

REFERENCES — the connection between beds made enforceable

So far every bed has stood alone. But gardens have relationships. Imagine a second bed alongside the rose bed — a bed of gardeners, each with their own id and name. Now imagine a third trait card, flower_beds, a catalogue of every bed in the garden — rose bed, herb bed, vegetable bed — where each entry says which gardener tends it. The way to say "this bed belongs to that gardener" is to store the gardener's id on the flower_beds row.

But storing a number is just a number. Nothing stops you from writing gardener_id = 99 on a flower_beds row when no gardener with id 99 actually exists. The pointer would point at nothing, and the bed would never notice.

A FOREIGN KEY turns that pointer into a promise. The bed refuses any value for the referencing column that doesn't match an id in the referenced table. Two trait cards, with a line drawn from one to the other — and the line is now enforced.

greenhouse.sql
the workbench
FOREIGN KEY
Two trait cards. The FK badge marks the link.
Field guide: FOREIGN KEY turns a pointer into a promise. The bed refuses any value for the referencing column that doesn't match an id in the referenced table — so a flower_beds row can't point at a non-existent gardener, and you can't delete a gardener someone is still pointing at (unless you opt into cascading behavior). The relationship becomes something the database understands, not just a convention the gardener remembers.

Page 12 · ALTER TABLE

Prune the trait card

add a column, drop one, rename one

Sometimes the trait card you drafted needs to change after the bed is planted. Maybe you forgot a column. Maybe a column turned out to be useless. SQL's ALTER TABLE lets you change the schema without redrafting from scratch.

greenhouse.sql
the workbench
ALTER TABLE
A new column added to an existing card.
Field guide: ALTER TABLE has three common forms: ADD COLUMN name type, DROP COLUMN name, and RENAME COLUMN old TO new. Different databases support slightly different things; check your dialect's docs for the edge cases.

Page 13 · DROP TABLE

Plow the whole bed under

DROP TABLE — the schema itself is removed

DROP TABLE doesn't just empty the bed — it removes the trait card entirely. After it, the bed simply isn't there. Any rows are gone. Any foreign keys that pointed at it now point at nothing.

This is the most destructive single statement in SQL. Run it on the wrong table and it cannot be undone (outside a transaction). Many real teams disable DROP TABLE permissions in production for exactly this reason.

greenhouse.sql
the workbench
DROP TABLE
Drafted, then plowed under. The workbench is empty again.
Field guide: DROP TABLE IF EXISTS name is the safer form for scripts — it won't fail if the table doesn't exist. Useful when you're tearing down a test setup.

Page 14 · Draft your own

Design a bed yourself

CREATE, ALTER, INSERT — try anything

The greenhouse is yours. Draft a trait card from scratch, insert rows to test the constraints, and watch what gets accepted or rejected. Reset wipes the greenhouse clean.

Try the beds we sketched on Page 2 — a herb_bed with (id, name, scent, perennial), a veg_bed with (id, kind, days_to_harvest) — or invent something the garden doesn't have yet. Several beds can coexist in the greenhouse at once, each with its own trait card.

scratch.sql
the workbench
your design
Draft a card, plant some flowers, watch what happens.
⌘/Ctrl + Enter to run
Try: a CHECK with color IN (...), a UNIQUE column on a second field (e.g. code TEXT UNIQUE alongside the primary key), or two related tables with a FOREIGN KEY linking them.

Page 15 · End of Chapter III

The trait card is drafted

what you've learned, and what's next

In the Greenhouse, no bed is dug and no flower is yet planted. Only a card is drafted, on which the columns of the future bed are named and the rules it will obey are stamped. By such cards, every bed in the garden is shaped before its first seed.

CREATE TABLE
draft a new trait card — name, columns, types, constraints
NOT NULL
column must always be filled in
PRIMARY KEY
the unique tag — implies NOT NULL and UNIQUE
UNIQUE
no two rows may share the same value in this column
DEFAULT
fallback value when the column is missing from INSERT
CHECK
rule the bed enforces — uses WHERE-style expressions
REFERENCES
foreign key — points at a row in another table
ALTER TABLE
change the trait card after the bed is planted
DROP TABLE
remove the trait card entirely

That closes Spring. Beyond it lies Summer, with two further gardens. One is called the Flight Path: it is travelled by bees, between the hive and the open flower, and it is never the same path twice. The other is called the Sorting Table: there the day's basket is set down, and its contents are placed into smaller baskets, one color to each. For today, the workbench is wiped clean, and the trait card filed away.

Check your trait card

Two questions to confirm CREATE TABLE clicks. Write the statement and press Check.

1. Draft a new bed. CREATE a table called tulip_bed with columns id INTEGER, color TEXT, height INTEGER.

CREATE TABLE tulip_bed ( id INTEGER, color TEXT, height INTEGER );

2. Draft herb_bed with constraints: id INTEGER PRIMARY KEY, name TEXT NOT NULL.

CREATE TABLE herb_bed ( id INTEGER PRIMARY KEY, name TEXT NOT NULL );

How did this chapter land?

Your verdict
· Sunset ·
❋
end of Chapter
III
the trait card is filed, the workbench cleared,
the beds beyond it have begun to talk to one another
❋
Spring is complete · Summer awaits