Chapter 14

Databases & SQL

High School
At a glance
Core ideaRelational data lives in tables linked by keys, queried declaratively.
Key termJOIN — combine rows across tables on a matching key.
You can…Write SELECT / JOIN / GROUP BY to answer real questions.
Watch outNormalise so each fact is stored once and update anomalies vanish.
Theory

A database is an organised, persistent store of data with efficient query and update. The dominant model is relational (Codd, 1970): data lives in tables (relations) of rows (records) and columns (fields). A primary key uniquely identifies each row; a foreign key in one table references the primary key of another, encoding relationships between tables.

SQL (Structured Query Language) is the declarative language for relational data. You state what you want, not how to fetch it — the database's query optimiser devises an efficient plan. Core verbs: SELECT (read), INSERT, UPDATE, DELETE, and JOIN (combine rows across tables on a matching key).

Normalisation organises tables to eliminate redundancy so that each fact is stored exactly once, preventing update anomalies. Transactions guarantee ACID properties: Atomicity (all-or-nothing), Consistency, Isolation (concurrent transactions don't interfere), and Durability (committed data survives crashes).

Explanation

The relational model's genius is separating logical structure from physical storage. You query by describing relationships between values; the engine decides which indexes to use, in what order to scan, how to join. This declarative style is why the same SQL runs efficiently on a laptop or a thousand-server cluster — the optimiser adapts, you don't rewrite.

Why normalise? Consider storing a customer's address alongside every one of their orders. Change address and you must update dozens of rows; miss one and your data contradicts itself — an update anomaly. Normalisation splits customers and orders into separate tables linked by a foreign key, so the address lives in exactly one place. The JOIN reassembles the full picture on demand.

Trade-off. Fully normalised data avoids anomalies but needs joins to read. High-scale systems sometimes denormalise (deliberately duplicate) to trade storage and update-complexity for read speed — a conscious engineering choice, not sloppiness.

Practical

We create two related tables, insert data, and answer a real question with a JOIN plus aggregation: "How much has each customer spent?"

-- Two tables linked by a foreign key
CREATE TABLE customers (
    id    INTEGER PRIMARY KEY,
    name  TEXT NOT NULL,
    city  TEXT
);

CREATE TABLE orders (
    id          INTEGER PRIMARY KEY,
    customer_id INTEGER REFERENCES customers(id),   -- foreign key
    amount      REAL NOT NULL
);

INSERT INTO customers VALUES (1, 'Aisha', 'Kuala Lumpur'),
                             (2, 'Ben',   'Singapore'),
                             (3, 'Chen',  'Kuala Lumpur');

INSERT INTO orders VALUES (10, 1, 250.00), (11, 1, 100.00),
                          (12, 2,  75.50), (13, 3, 400.00);

-- Total spend per customer, highest first (JOIN + GROUP BY + aggregate)
SELECT  c.name,
        c.city,
        COUNT(o.id)   AS num_orders,
        SUM(o.amount) AS total_spent
FROM        customers AS c
JOIN        orders    AS o  ON o.customer_id = c.id
GROUP BY    c.id, c.name, c.city
HAVING      SUM(o.amount) > 100          -- filter groups after aggregation
ORDER BY    total_spent DESC;

-- Result:
-- name  | city          | num_orders | total_spent
-- Chen  | Kuala Lumpur  |     1      |   400.00
-- Aisha | Kuala Lumpur  |     2      |   350.00

Read it declaratively: JOIN stitches each order to its customer via the key; GROUP BY collapses rows per customer; SUM/COUNT aggregate each group; HAVING filters the groups; ORDER BY sorts. Ben is excluded because his 75.50 fails the HAVING test. You never told the engine how to loop — only what you wanted.

Q&A
Q1 What is the difference between a primary key and a foreign key?

A primary key uniquely identifies each row within its own table and cannot repeat or be null. A foreign key is a column that points to another table's primary key, establishing a relationship. In the example, customers.id is a primary key; orders.customer_id is a foreign key referencing it, so every order is tied to exactly one customer.

Q2 Why is SQL called "declarative"?

Because you describe the result you want, not the step-by-step procedure to compute it. You write "give me customers who spent over 100, sorted by total"; the database's optimiser figures out which indexes to use and how to execute it efficiently. Compare imperative code, where you'd write the loops and comparisons yourself.

Q3 What does the "A" in ACID protect against?

Atomicity ensures a transaction is all-or-nothing. Transferring money debits one account and credits another; if the system crashes between the two steps, atomicity guarantees both are rolled back, never leaving money that vanished from one account without arriving in the other. The transaction either fully commits or fully aborts.

Q4 When would I choose a NoSQL database over a relational one?

When your data is unstructured or schema-fluid (documents, key-value pairs), when you need to scale writes across many machines horizontally, or when you can trade strict consistency for availability and speed. NoSQL stores relax parts of the relational model and ACID to gain flexibility and scale — but you give up powerful joins and strong guarantees, so relational remains the default for data with rich relationships.

Concept mind map

How the ideas connect

Every key idea in this chapter, branching from the core concept — use it to see the whole picture at a glance.

tablerow and columnprimary keyforeign keyJOINGROUP BYDatabases and SQL
Infographic

The key facts, visualised

Table
Rows (records) and columns (fields) holding related data.
Primary key
A column whose value uniquely identifies each row.
JOIN
Combine rows across tables on a matching key.
GROUP BY
Bucket rows by a column to aggregate, e.g. COUNT or SUM.
Solved examples

Worked problems, step by step

Follow each solution line by line, then try to reproduce it on paper before moving on.

Example 1Write SQL to get the names of students older than 18.

  1. Choose the name column.
  2. Filter rows with a WHERE clause.

Example 2Write SQL to count orders per customer.

  1. Group the rows by customer_id.
  2. Count rows in each group.
Practice problem set

Now you try

Work each one out first, then tap to reveal the worked answer.

1What is a relational table?
A grid of rows (records) and columns (fields) storing related data.
2What is a primary key?
A column (or set of columns) whose value uniquely identifies each row in a table.
3What does a JOIN do?
It combines rows from two or more tables where a key column matches.
4What is a foreign key?
A column that references the primary key of another table, linking the two.
5What does GROUP BY do?
It buckets rows sharing a column value so you can aggregate each bucket with COUNT, SUM, or AVG.
6Why is SQL called declarative?
You state what result you want, not the step-by-step procedure to compute it; the database plans the how.