A JSON file works until two users write at the same time, until you need to filter a million records, or until a half-written file corrupts everything. Databases exist to solve those problems once, properly.
Learning objectives
- Explain what a relational database stores and how tables relate.
- Distinguish primary keys, foreign keys and unique constraints.
- Model one-to-many and many-to-many relationships.
- Decide when SQLite is enough and when a server database is needed.
- Compare relational thinking with document databases.
1. Tables, rows, columns
A relational database stores data in tables. Each row is one record; each column has a type and a meaning.
users
id | username | email | created_at
1 | kim | [email protected] | 2026-01-04
2 | lee | [email protected] | 2026-01-05
The schema — table names, columns, types, constraints — is the contract. The application writes data that fits it, and the database rejects anything that does not.
2. Keys and constraints
Primary key — uniquely identifies a row.
id INTEGER PRIMARY KEY AUTOINCREMENT
Foreign key — a column that points at another table's primary key. It is how relationships are enforced.
user_id INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
Other constraints:
| Constraint | Meaning |
|---|---|
NOT NULL |
value is required |
UNIQUE |
no duplicate values (emails, usernames) |
DEFAULT |
value used when none is supplied |
CHECK |
value must satisfy an expression (price >= 0) |
Constraints are cheaper than application bugs. A duplicate email rejected by the database cannot slip through a race condition in your code.
3. Relationships
One-to-many — one user writes many posts. The foreign key lives on the "many" side:
users 1 ─── N posts (posts.user_id → users.id)
Many-to-many — an order contains many products, a product appears in many orders. SQL cannot store a list in a column, so you add a join table:
orders 1 ─── N order_items N ─── 1 products
order_items holds order_id, product_id, quantity and the price at the time of purchase. Copying the price is deliberate: product prices change, order history must not.
One-to-one — a user has one profile. Use a separate table with a unique foreign key, or merge the tables if the data is always present.
4. SQLite
SQLite is a complete relational database stored in a single file. No server, no accounts, no ports.
Good fit: learning, prototypes, desktop and mobile apps, small sites, tests, embedded tools.
Not the right fit: many concurrent writers, multi-server deployments, datasets that need replicas and failover. For those, use PostgreSQL or MySQL — the SQL you learn here transfers almost unchanged.
5. Relational vs NoSQL
| Relational (SQL) | Document (MongoDB) |
|---|---|
| Tables with fixed columns | Collections of flexible documents |
| Relations via foreign keys | Embedding or references |
| Strong constraints | Validation in the application or schema |
| JOINs across tables | Usually data shaped for one query |
Neither is "better". Relational suits data with clear relationships and integrity requirements — orders, payments, enrolment. Document stores suit nested, variable-shaped content — logs, catalogues, CMS pages. Pick based on how the data is read and how strictly it must be consistent.
6. Normalisation in one paragraph
Store each fact once. If a customer's address lives in every order row, updating it means updating hundreds of rows and missing one creates contradictory data. Move the address to customers and reference it. The exception is deliberate denormalisation for read performance — but make that a decision, not an accident.
Practice
Design the shop schema in the assignment: users, products, orders and order items. Draw the relationships before writing any SQL.