Chapter 14 ยท Databases & Forms
Databases, Tables & SQL
So far, all our data vanishes the instant the page finishes loading โ variables live only for one request. Real applications need memory that survives: a database. This chapter introduces MySQL, tables, and the language used to talk to them: SQL.
A database is a filing cabinet. Each table is one drawer dedicated to one kind of thing (a drawer for students, a drawer for courses). Inside a drawer, every row is one filled-in record card (one actual student), and the columns are the labelled blanks that every card shares (name, program, fee). SQL is the polite language you use to ask the filing clerk to add, find, change or remove cards.
Meet phpMyAdmin
XAMPP ships with phpMyAdmin, a friendly web page for managing MySQL. With Apache and MySQL running, visit:
http://localhost/phpmyadmin
You'll see existing databases on the left. We'll create our own. You can do everything by clicking buttons โ but we'll learn the SQL commands too, because buttons don't work from PHP; SQL does. In phpMyAdmin, click the SQL tab to type commands directly.
Creating a database and a table
CREATE DATABASE school_db;
USE school_db;
CREATE TABLE students (
id INT AUTO_INCREMENT PRIMARY KEY,
student_number VARCHAR(20) NOT NULL UNIQUE,
full_name VARCHAR(100) NOT NULL,
program VARCHAR(100) NOT NULL,
fee_balance DECIMAL(10,2) DEFAULT 0.00,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Let's decode every piece โ this vocabulary appears in every project you will ever build:
| Piece | Plain meaning |
|---|---|
INT | Whole numbers |
VARCHAR(100) | Text up to 100 characters |
DECIMAL(10,2) | Exact decimal number: up to 10 digits, 2 after the point โ always use this for money, never FLOAT |
TIMESTAMP | A date and time |
NOT NULL | This blank may never be left empty |
UNIQUE | No two rows may share this value |
DEFAULT ... | The value used if none is provided |
AUTO_INCREMENT | MySQL numbers the rows itself: 1, 2, 3โฆ |
PRIMARY KEY | The column that uniquely identifies each row โ its "NRC number" |
Names can repeat (two students called John Banda!), but the auto-incrementing id
never does. When we later edit or delete a record, we always point at it by id. Every table you
ever design should start with id INT AUTO_INCREMENT PRIMARY KEY.
INSERT โ adding rows
INSERT INTO students (student_number, full_name, program, fee_balance)
VALUES ('LGU2026-001', 'Chanda Mwila', 'BSc Computing', 5000.00);
INSERT INTO students (student_number, full_name, program, fee_balance)
VALUES
('LGU2026-002', 'Mutinta Habeenzu', 'LLB', 7200.00),
('LGU2026-003', 'Bwalya Kunda', 'BBA', 4500.00),
('LGU2026-004', 'Thandiwe Phiri', 'BSc Computing', 6100.00);
Notice we never mention id or created_at โ AUTO_INCREMENT and
DEFAULT fill them automatically. Text values go in single quotes; numbers don't.
SELECT โ reading rows (the command you'll use most)
-- Everything from the drawer ( * means "all columns")
SELECT * FROM students;
-- Only some columns
SELECT full_name, program FROM students;
-- Filtering with WHERE
SELECT * FROM students WHERE program = 'BSc Computing';
-- Comparison and combining conditions
SELECT * FROM students WHERE fee_balance > 5000 AND program = 'LLB';
-- Pattern search: names containing "anda" anywhere (% = "anything here")
SELECT * FROM students WHERE full_name LIKE '%anda%';
-- Sorting: highest balance first
SELECT * FROM students ORDER BY fee_balance DESC;
-- Only the first 2 rows
SELECT * FROM students ORDER BY created_at DESC LIMIT 2;
Result of the first query โ this shape (rows ร columns) is what PHP will receive in the next chapter:
UPDATE โ changing rows
UPDATE students
SET fee_balance = 3500.00
WHERE id = 1;
Forget the WHERE clause and every single row is updated.
UPDATE students SET fee_balance = 0; just made the whole university free. The same
danger applies to DELETE. Ritual to adopt for life: write the WHERE first, then go back and
write the rest.
DELETE โ removing rows
DELETE FROM students WHERE id = 4;
Two tables and the idea of relationships
Real systems have many drawers that reference each other. A courses table, and a marks table that links students to courses:
CREATE TABLE courses (
id INT AUTO_INCREMENT PRIMARY KEY,
code VARCHAR(10) NOT NULL UNIQUE,
title VARCHAR(100) NOT NULL
);
CREATE TABLE marks (
id INT AUTO_INCREMENT PRIMARY KEY,
student_id INT NOT NULL,
course_id INT NOT NULL,
score INT NOT NULL,
FOREIGN KEY (student_id) REFERENCES students(id),
FOREIGN KEY (course_id) REFERENCES courses(id)
);
A foreign key is a column that holds another table's id โ a "see drawer S,
card 3" note. It links the drawers together and MySQL refuses marks pointing at students who
don't exist. To read linked data back in one combined result, SQL offers JOIN:
SELECT s.full_name, c.code, m.score
FROM marks m
JOIN students s ON s.id = m.student_id
JOIN courses c ON c.id = m.course_id;
Don't worry about mastering JOINs today โ our project uses a single table, and you can return to JOINs once the basics feel comfortable. Just remember: ids link tables together.
In phpMyAdmin's SQL tab: (1) create the school_db database and
students table from this chapter, (2) insert the four students, (3) write a SELECT
that shows only BSc Computing students ordered by fee_balance, highest first.
Show the query for step 3
SELECT * FROM students
WHERE program = 'BSc Computing'
ORDER BY fee_balance DESC;
Which SQL command reads data from a table?
What does DELETE FROM students; (no WHERE) do?
Which column type should store money?
- Database = filing cabinet, table = drawer, row = record card, column = labelled blank.
- Every table starts with
id INT AUTO_INCREMENT PRIMARY KEY. - The CRUD four:
INSERT(create),SELECT(read),UPDATE(update),DELETE(delete). WHEREfilters; forgetting it on UPDATE/DELETE affects every row. Write WHERE first!ORDER BY ... DESCsorts;LIKE '%x%'pattern-searches;LIMITcaps the rows.- Use
DECIMALfor money. Foreign keys (ids) link tables; JOIN reads them together.