/knowledge/notes/functional-dependencies
Concept note · Database Systems
Functional Dependencies
A dependency says which columns fix which. Keys, closures and normal forms all follow from taking those promises seriously.
- Studied
- Database SystemsINFO20003 · COMP90050
- When
- 2020 S1
- Applied in
- Louvre Ops DB
- Read / Refreshed
- ~6 min read2026-10-21
A functional dependency is a promise about data: whenever two rows agree on these columns, they agree on that one too. A good part of database design is writing those promises down and then arranging the tables so that each fact is stored in exactly one place. This note covers what a dependency says, how to reason from a few of them to everything they imply, and why a table that ignores them turns every update into a chance to contradict itself.
01
The idea
Write , read " determines ", when knowing the values in columns fixes the value in column . In a ticketing table, the ticket number determines the ticket type, and the type determines the price. The dependency is a claim about every table the system could ever hold, not only the rows in it today. That makes it a business rule, and it comes from what the columns mean.
A key is a set of columns that determines every other column, with nothing spare in it. Declaring a primary key is declaring a dependency from the key to the whole row, which is why a key chosen carelessly can quietly stop the table storing what actually happens.
Trouble starts when a dependency's left side is not a key. Picture one wide table of wing scans with the ticket type and price copied into every row. The Adult price now sits in every Adult scan. Change it and you must update every one of those rows, and if one is missed, the table holds two Adult prices at once. That is an update anomaly. You also cannot record the price of a new ticket type until someone is scanned with one, and deleting the last Member scan forgets what a Member ticket costs. Normalisation fixes all three by splitting the table so each dependency's left side becomes the key of its own table. The Database Systems topic walks through the normal forms that make this precise.
02
The maths
Formally, a relation satisfies when no two of its rows agree on and disagree on .
One pair of rows is enough to break a dependency. No number of rows is enough to prove one.
From a declared set of dependencies , Armstrong's three rules derive every dependency that must also hold: reflexivity, augmentation and transitivity.
The rules are sound, so they never derive a false dependency, and complete, so they derive every true one.
In practice you rarely apply the rules by hand. You compute a closure instead: start with , and whenever a declared dependency's left side sits inside the set, add its right side, until nothing changes.
A key is a superkey that stops being one if any column is removed.
Boyce–Codd normal form asks that every dependency that holds has a superkey on its left. Third normal form relaxes this slightly, allowing the right side to be part of some key, which keeps every dependency checkable inside a single table when BCNF cannot.
Trivial means Y is already inside X, as in ticket, time determines time.
A split must not lose information. Joining the pieces back has to give exactly the original rows, no more, and that is guaranteed when the shared columns are a key of one of the pieces.
Split on a dependency X to Y by making one table of X and Y, and keeping X in the other.
03
Try it
The widget holds a log of wing scans at a made-up museum, one row per scan, and the four rules its designer declared. Build a dependency with the buttons. The widget answers two separate questions about it: does this sample of rows break it, and does it follow from the declared rules?
Rules the designer declared
- ticket → type
- type → price
- wing → level
- ticket, time → wing
Left side, X (pick one or more)
ticket → wing
In this sample
Broken. Rows 1 and 4 agree on ticket (T08) but differ on wing: East against West.
From the declared rules
{ticket}⁺ = {ticket, type, price}. The closure does not contain wing, so the rules do not promise this. The left side is not a key.
Via ticket → type, then type → price.
| # | ticket (X) | type | price | wing (Y) | level | time |
|---|---|---|---|---|---|---|
| 1clash | T08 | Member | 0 | East | L2 | 09:10 |
| 2 | T03 | Adult | 22 | East | L2 | 09:14 |
| 3 | T01 | Adult | 22 | West | L0 | 09:17 |
| 4clash | T08 | Member | 0 | West | L0 | 09:18 |
| 5 | T01 | Adult | 22 | East | L2 | 09:57 |
| 6 | T08 | Member | 0 | North | L1 | 09:59 |
| 7 | T01 | Adult | 22 | West | L0 | 10:02 |
| 8 | T02 | Adult | 22 | West | L0 | 10:25 |
| 9 | T05 | Adult | 22 | West | L0 | 10:41 |
| 10 | T04 | Member | 0 | West | L0 | 10:53 |
| 11 | T02 | Adult | 22 | West | L0 | 11:08 |
| 12 | T04 | Member | 0 | East | L2 | 11:12 |
| 13 | T04 | Member | 0 | North | L1 | 11:54 |
| 14 | T06 | Adult | 22 | East | L2 | 12:10 |
| 15 | T07 | Adult | 22 | North | L1 | 12:38 |
| 16 | T06 | Adult | 22 | West | L0 | 12:43 |
Broken. Rows 1 and 4 agree on ticket (T08) but differ on wing: East against West. The closure does not contain wing, so the rules do not promise this.
- The default asks whether ticket determines wing. The data breaks it, because a ticket visits several wings, and the closure of ticket holds only ticket, type and price.
- Add time to the left side. Ticket and time together determine the wing, the closure reaches every column, and the pair is a key.
- Try price determines type. At 16 rows it holds. Drag the rows to 24, and a Child ticket and a Member ticket turn out to cost the same 0. Type determines price, and the reverse was never a rule.
- Try time determines ticket. Up to 32 rows every scan falls on a different minute, so the sample cannot refute it. At 40 rows several minutes are shared. A sample can break a dependency and never prove one.
- Tick the split. Three declared rules have a left side that is not a key, and splitting on them stores fewer cells and keeps the Adult price in one row.
04
Where I used it
05
Easy to get wrong
06
Sources
- A relational model of data for large shared data banksCodd, Communications of the ACM 13(6), 1970, pp. 377–387.The paper that started relational databases, including the first discussion of normalising relations.
- A simple guide to five normal forms in relational database theoryKent, Communications of the ACM 26(2), 1983, pp. 120–125.Short and plain, with one example per normal form. Still the quickest way to see what each form forbids.
- Database Management SystemsRamakrishnan and Gehrke, 3rd edition, McGraw-Hill, 2003. Chapter 19.A standard textbook chapter on schema refinement: closures, keys, BCNF and 3NF, and lossless, dependency-preserving decompositions.