SQL Schema Design Quiz: Keys, Normal Forms and Views Practice
Test yourself: 12 questions and three scripts on CREATE TABLE, data types, constraints, ALTER TABLE, normalization from 1NF to BCNF, foreign keys and views.
- Course: SQL study plan
- Module: Schema design
- Kind: Checkpoint — cleared at 70%
- Reading time: 25 min
- Runtime: MySQL 8 syntax on SQLite 3.49
Checkpoint — Schema design is the checkpoint that closes the Schema design module: a graded quiz and whole-program exercises, passed at 70%.
Instructions
This checkpoint covers the whole module: CREATE TABLE and choosing types (integers, DECIMAL for money, VARCHAR and CHAR, DATE, DATETIME and TIMESTAMP, BOOLEAN), CREATE TABLE ... AS SELECT; the constraints NOT NULL, DEFAULT, PRIMARY KEY, UNIQUE, CHECK and FOREIGN KEY; ALTER TABLE and DROP; normalization from 1NF to BCNF; one-to-many, many-to-many and self-referencing relationships with their ON DELETE actions; and views.
How it works. Twelve questions and three scripts. You need 70% on the questions and every script accepted to clear the module. You can retake it as often as you like; your best score counts. Each script builds or changes a schema and ends with the SELECT that shows the result.
Before you start, make sure you can answer these from memory:
- Why is money stored as
DECIMALand notFLOAT, and what doesBOOLEANreally mean in MySQL? - What does
CREATE TABLE ... AS SELECTcopy, and what does it leave behind? - Primary key vs unique key: how many per table, and which may hold NULL?
- How do you add a NOT NULL column to a table that already has rows?
- What do 1NF, 2NF, 3NF and BCNF each forbid?
- Where does the foreign key go in a one-to-many relationship, and how is many-to-many stored?
- What is the difference between a view and a table, and how do you change a view's definition?
The three scripts are a library schema built from a flat loan log, a migration that adds, backfills and renames columns, and a reporting view over a many-to-many relationship.
Common questions
What is the difference between a primary key and a unique key?
A table has one primary key, which is never NULL and in InnoDB is the clustered index ordering the rows. It can have many unique keys, which also reject duplicates but may hold NULLs, any number of them in MySQL.
Which normal form does a partial dependency break?
Second normal form. A partial dependency is a non-key column that depends on only part of a composite key, such as a product name in an order-lines table keyed by order and product. Moving it to a products table fixes it.
Does a view store data?
No. A view stores its SELECT, and each read runs that query on the current base tables, so it is always up to date and takes no space. A materialized view, available in PostgreSQL and Oracle but not MySQL, is the kind that stores its result.
Exercises
A library schema from a loan log
loan_log(id, member_email, member_name, isbn, title, borrowed_on) is a flat record of every loan. Normalize it into three tables:
members(id INT AUTO_INCREMENT PRIMARY KEY, email VARCHAR(100) NOT NULL UNIQUE, name VARCHAR(50) NOT NULL);books(isbn VARCHAR(13) PRIMARY KEY, title VARCHAR(100) NOT NULL);loans(member_id INT NOT NULL, isbn VARCHAR(13) NOT NULL, borrowed_on DATE NOT NULL)with a primary key on all three columns and foreign keys tomembersandbooks.
Fill members in email order (so ids are numbered alphabetically by email), then books, then loans (translating emails to member ids with a join). Print the members as id, email ordered by id, then each member's loan count as name, loans ordered by loans descending, then name.
| id | member_email | member_name | isbn | title | borrowed_on |
|---|---|---|---|---|---|
| 1 | ravi@lib.io | Ravi | 9780132350884 | Clean Code | 2024-03-01 |
| 2 | asha@lib.io | Asha | 9780596007126 | Head First Design Patterns | 2024-03-02 |
| 3 | ravi@lib.io | Ravi | 9780596007126 | Head First Design Patterns | 2024-03-10 |
| 4 | meera@lib.io | Meera | 9780132350884 | Clean Code | 2024-03-12 |
| 5 | ravi@lib.io | Ravi | 9780132350884 | Clean Code | 2024-04-01 |
id | email
1 | asha@lib.io
2 | meera@lib.io
3 | ravi@lib.io
name | loans
Ravi | 3
Asha | 1
Meera | 1Migrate the price column
products stores prices as integer cents and carries a dead column. Migrate it, in order:
- add
price DECIMAL(10,2); - backfill it as
price_cents / 100; - drop
price_cents; - rename
titletoname; - drop
old_sku; - add
currency CHAR(3) NOT NULL DEFAULT 'INR'.
Then print SELECT * FROM products ORDER BY id — the columns are id, name, price, currency.
| id | title | price_cents | old_sku |
|---|---|---|---|
| 1 | Pen | 150 | X1 |
| 2 | Notebook | 1999 | X2 |
| 3 | Bag | 120000 | NULL |
id | name | price | currency
1 | Pen | 1.5 | INR
2 | Notebook | 19.99 | INR
3 | Bag | 1200 | INRA course roster view
Create a view course_roster with the columns code, student, enrolled_on — one row per enrolment, joining enrolments to courses and students.
The starter then enrols Ben (student 2) in course 1 and reads the view twice: the roster of course DB101 as student ordered by name, and the number of students per course as code, students ordered by code. Leave those statements as they are.
students: (1, Asha), (2, Ben), (3, Chen). courses: (1, DB101), (2, OS201). enrolments(student_id, course_id, enrolled_on): (1, 1, 2024-01-10), (3, 1, 2024-01-12), (1, 2, 2024-01-15), (2, 2, 2024-01-20).
student
Asha
Ben
Chen
code | students
DB101 | 3
OS201 | 2In this module: Schema design
← Views — saved queries as tables · How indexes work — B+trees, clustered and secondary →