- This statement asks you to Construct a normalized design, so the work here is applying the normal forms and their dependency rules rather than defining them again.
- The scenario always arrives as one flat table or a description of a paper form, and your job is to end up with a set of tables and keys.
- The method below works on any scenario, and the two worked cases show it applied to different shapes of problem.
- Write out the unnormalized table
- List every attribute the scenario mentions, with two or three sample rows, because the repeated values in the sample data are what reveal the dependencies.
- Split any cell holding more than one value
- Move the repeating group into its own entity and give it a foreign key back to the original, which reaches 1NF.
- Write down the primary key you are left with
- Use the natural composite key at this stage rather than inventing an ID, because a composite key is what makes a partial dependency visible.
- Test every non-key attribute against the whole key
- Ask "do I need all of the key to know this value?" and move out anything that answers no, which reaches 2NF.
- Test every non-key attribute against the other non-key attributes
- Ask "is this value really determined by another ordinary column?" and move out anything that answers yes, which reaches 3NF.
- Re-check every table you created along the way
- A table produced at the 2NF step can itself contain a transitive dependency, and that is the step most often skipped.
- Work with sample data in front of you rather than with column names alone.
- Any value you can see written twice in your sample rows is pointing at a dependency you have not moved yet.
- Name each table after the single thing one of its rows describes, and if you cannot name it in one word the table is probably holding two entities.
A clinic records appointments on a single spreadsheet, and each row holds the patient, the doctor, the doctor's department and the tests ordered.
| PatientID | PatientName | PatientPhone | ApptDate | ApptTime | DoctorID | DoctorName | Specialism | DeptName | DeptHead | TestsOrdered |
|---|
| P01 | Maya Okafor | 020 7946 0102 | 2026-03-04 | 09:00 | D11 | Dr Silva | Cardiology | Cardiology | Dr Ahmed | ECG, Blood panel |
| P02 | Tom Reid | 020 7946 0155 | 2026-03-04 | 09:30 | D11 | Dr Silva | Cardiology | Cardiology | Dr Ahmed | ECG |
| P01 | Maya Okafor | 020 7946 0102 | 2026-03-11 | 14:00 | D24 | Dr Baptiste | Dermatology | Dermatology | Dr Lin | Skin biopsy |
- TestsOrdered holds a list, so it is not atomic and it becomes its own entity.
- An appointment is identified naturally by which patient came at which date and time, so that triple is the key of what remains.
- Appointment (PK PatientID + ApptDate + ApptTime)
| PatientID | ApptDate | ApptTime | PatientName | PatientPhone | DoctorID | DoctorName | Specialism | DeptName | DeptHead |
|---|
| P01 | 2026-03-04 | 09:00 | Maya Okafor | 020 7946 0102 | D11 | Dr Silva | Cardiology | Cardiology | Dr Ahmed |
| P02 | 2026-03-04 | 09:30 | Tom Reid | 020 7946 0155 | D11 | Dr Silva | Cardiology | Cardiology | Dr Ahmed |
| P01 | 2026-03-11 | 14:00 | Maya Okafor | 020 7946 0102 | D24 | Dr Baptiste | Dermatology | Dermatology | Dr Lin |
AppointmentTest (PK PatientID + ApptDate + ApptTime + TestName, all three date and patient columns FK)
| PatientID | ApptDate | ApptTime | TestName |
|---|
| P01 | 2026-03-04 | 09:00 | ECG |
| P01 | 2026-03-04 | 09:00 | Blood panel |
| P02 | 2026-03-04 | 09:30 | ECG |
| P01 | 2026-03-11 | 14:00 | Skin biopsy |
- PatientName and PatientPhone are determined by PatientID alone, so they depend on part of the composite key.
- The evidence is visible in the sample rows: Maya Okafor's phone number is written twice.
- Patient (PK PatientID)
| PatientID | PatientName | PatientPhone |
|---|
| P01 | Maya Okafor | 020 7946 0102 |
| P02 | Tom Reid | 020 7946 0155 |
Appointment (PK PatientID + ApptDate + ApptTime, FK PatientID)
| PatientID | ApptDate | ApptTime | DoctorID | DoctorName | Specialism | DeptName | DeptHead |
|---|
| P01 | 2026-03-04 | 09:00 | D11 | Dr Silva | Cardiology | Cardiology | Dr Ahmed |
| P02 | 2026-03-04 | 09:30 | D11 | Dr Silva | Cardiology | Cardiology | Dr Ahmed |
| P01 | 2026-03-11 | 14:00 | D24 | Dr Baptiste | Dermatology | Dermatology | Dr Lin |
- DoctorName, Specialism, DeptName and DeptHead are all determined by DoctorID, and DoctorID is not part of the key, so this is a transitive dependency.
- Moving them into a Doctor entity leaves a second transitive dependency inside that new table, because DeptHead is determined by DeptName rather than by DoctorID.
- Both have to be removed, which is why step six of the procedure matters.
- Department (PK DeptID)
| DeptID | DeptName | DeptHead |
|---|
| 1 | Cardiology | Dr Ahmed |
| 2 | Dermatology | Dr Lin |
Doctor (PK DoctorID, FK DeptID)
| DoctorID | DoctorName | Specialism | DeptID |
|---|
| D11 | Dr Silva | Cardiology | 1 |
| D24 | Dr Baptiste | Dermatology | 2 |
Appointment (PK PatientID + ApptDate + ApptTime, FK PatientID, FK DoctorID)
| PatientID | ApptDate | ApptTime | DoctorID |
|---|
| P01 | 2026-03-04 | 09:00 | D11 |
| P02 | 2026-03-04 | 09:30 | D11 |
| P01 | 2026-03-11 | 14:00 | D24 |
- The three-column key for Appointment is correct but awkward, because AppointmentTest has to carry all three columns as its foreign key.
- At implementation you would add a surrogate key called AppointmentID and let AppointmentTest reference that single column instead.
- Adding a surrogate key does not change the normal form, because it does not change which attribute determines which.
- Introducing it too early is a trap, because a single-column key hides the partial dependency that the 2NF step exists to find.
| Table | Primary key | Foreign keys | Other attributes |
|---|
| Patient | PatientID | none | PatientName, PatientPhone |
| Department | DeptID | none | DeptName, DeptHead |
| Doctor | DoctorID | DeptID | DoctorName, Specialism |
| Appointment | AppointmentID | PatientID, DoctorID | ApptDate, ApptTime |
| AppointmentTest | AppointmentID + TestName | AppointmentID | none |
- Stopping after the first split is the most common error, and it leaves the design in 2NF.
- Leaving Specialism in the Appointment table would be a transitive dependency, so it has to travel with the doctor.
- Forgetting the foreign key when you move attributes out breaks the link, and the two tables can then no longer be joined back together.
- Show the key of every table you produce, because a set of tables without keys does not demonstrate a normal form at all.
- The library's flat loan sheet holds MemberName, MemberEmail, BookTitle, AuthorName, ISBN, CopyBarcode, DateBorrowed and DateDue.
- The modelling decision that carries this scenario is the difference between a title and a physical copy.
- A loan is of one copy, and the library may hold six copies of the same ISBN, so a design that lends a title cannot say which book is on the shelf.
- AuthorName is determined by ISBN and not by the loan, so it is a transitive dependency and moves out at 3NF.
| Table | Primary key | Foreign keys | Other attributes |
|---|
| Member | MemberID | none | MemberName, MemberEmail |
| Author | AuthorID | none | AuthorName |
| Book | ISBN | AuthorID | BookTitle |
| Copy | CopyBarcode | ISBN | Condition |
| Loan | LoanID | CopyBarcode, MemberID | DateBorrowed, DateDue |
A book with two authors would make Book to Author many-to-many, which needs a linking table rather than a single AuthorID column.
- Test the finished library design against a question the library actually asks.
- "Which copies of this ISBN are out on loan today?" joins Copy to Loan on CopyBarcode.
- "How many books has this member borrowed?" joins Loan to Member on MemberID.
- A design that cannot answer the scenario's own questions has usually lost an entity somewhere.
The same three questions produce these entity sets, and each of these scenarios is named in the syllabus.
| Scenario | Typical 3NF entities | The dependency that catches people out |
|---|
| E-commerce platform | Customer, Product, Category, Order, OrderLine | The unit price belongs to Product, not to OrderLine, unless the price at the time of sale must be kept |
| School management | Student, Teacher, Class, Subject, Enrolment | Subject details depend on the class, not on the enrolment |
| Employee management | Employee, Department, JobRole, Payslip | Department manager depends on the department, not on the employee |
| Inventory management | Product, Supplier, Warehouse, StockMovement | Supplier contact details depend on the supplier, not on the product |
| Police crime reporting | Officer, Station, Incident, Suspect, IncidentSuspect | Station address depends on the station, not on the reporting officer |
- The e-commerce case is worth reading twice, because keeping the price paid on the order line is a deliberate, justified exception rather than a normalization error.
- A historical price is a different fact from the current price, and choosing to store it is closer to a denormalization decision than to a dependency you have missed.
- Why should you keep the natural composite key while working towards 2NF instead of adding an ID column straight away?
- In the hospital design, name the two transitive dependencies removed at the 3NF step.
- Why does the library design need both a Book table and a Copy table?
- A gym stores MemberID, MemberName, ClassID, ClassName and InstructorName in one table. Construct the 3NF tables and give each key.
- What goes wrong if you move attributes into a new table but forget to add the foreign key?