Normal forms: the key, the whole key, nothing but the key

After this lesson you will read the functional dependencies of a table, say which normal form it is in, split it step by step into tables that store every fact once — and say why the NoSQL half of the course builds the unsplit table on purpose.

Pine Valley Furniture's invoice table on p. 130 has five rows, and every value in it is right. Order 1006 really went to Value Furniture in Plano, TX; product 4 really is a Natural Maple entertainment center at 650.00. The slide still stamps it: "this is relation, but not a well-structured one".

Do three ordinary things with it. Value Furniture moves: you must edit three cells, and if you miss one the table contradicts itself. The company adds a new product to its catalogue: there is no row to put it in until somebody orders it. Furniture Gallery cancels its dresser: delete that line, and the company no longer knows that it sells a 4–Dr oak dresser at 500.00.

How can a table with no wrong value be wrong? Hold on to the question. The answer is one idea, and by the end of the lesson you will point at the exact arrow that causes each of the three troubles.

One fact, one place: what normalization is for

Start with the table itself. The highlighted cells below are not errors. Each of them repeats a fact that an earlier row already stated.

The INVOICE relation of p. 130 (Pine Valley Furniture), primary key underlined as on the slide. Highlighted: the 15 cells that repeat a fact already stored in an earlier row.
Order_IDOrder_DateCustomer_IDCustomer_NameCustomer_AddressProduct_IDProduct_DescriptionProduct_FinishUnit_PriceOrdered_Quantity
100610/24/20042Value FurniturePlano, TX7Dining TableNatural Ash800.002
100610/24/20042Value FurniturePlano, TX5Writer's DeskCherry325.002
100610/24/20042Value FurniturePlano, TX4Entertainment CenterNatural Maple650.001
100710/25/20046Furniture GalleryBoulder, CO114–Dr DresserOak500.004
100710/25/20046Furniture GalleryBoulder, CO4Entertainment CenterNatural Maple650.003

"Value Furniture is at Plano, TX" is stored three times, once per line of its order. "Product 4 is a Natural Maple Entertainment Center at 650.00" is stored twice, once per order that contains it. None of these copies is wrong today. Each copy is a place where the table can become wrong tomorrow.

The slides define the cure on p. 120:

Normalization — "A two step process that puts data into tabular form by removing repeating groups and then removes duplicated data from the relational tables." Normalization theory "is based on the concepts of normal forms".

The two steps are different in kind. The first makes the data a relation at all: one value per cell, a key per row. That is first normal form, section 3. The second splits tables so that each fact is stored once, guided by rules called functional dependencies. That gives second and third normal form, section 4.

The goal, printed in bold on p. 121, is "to create a set of relational tables that are free of redundant data and that can be consistently and correctly modified or updated", and the slide draws the consequence: "all tables in a relational database should be in the third normal form (3NF)". Page 127 lists the same goals as two bullets — "Eliminate redundant data" and "Ensure data dependencies make sense (only storing related data in a table)" — and adds the benefits: less space, data "logically stored and easy to maintain".

What goes wrong in an unnormalized table has standard names in the textbooks; the slides describe the goal and leave the names out. They are the three troubles of the puzzle:

Page 122 compresses third normal form into one sentence: "A relational table is in 3NF if and only if all non-key columns are: Mutually independent and Fully dependent upon the primary key", and calls 1NF and 2NF "intermediate steps to achieve the goal of having all tables in 3NF". A mnemonic that is not on the slides, usually credited to William Kent (1983), says the same thing in three steps: every non-key column depends on the key, the whole key, and nothing but the key. The first phrase is 1NF, the second 2NF, the third 3NF, and the rest of this lesson unpacks them in that order.

Normalization stores each fact once: every non-key column depends on the key, the whole key, and nothing but the key.

In a 1993 interview Edgar F. Codd, the inventor of the relational model, explained the word: "I called it normalization because then President Nixon was talking a lot about normalizing relations with China. I figured that if he could normalize relations, so could I." (Interview in DBMS Magazine, December 1993, as quoted by Joe Reis and by Simon Willison.)

Functional dependencies: the rules behind the repetition

Why is "Voyager" stored twice in the table of p. 125? Because Janeway likes two drinks, so she has two rows, and each row carries her address. The slide circles the repeated cells and writes the reason under each: "Because name -> addr".

The Drinkers instance of p. 125. Highlighted, as the slide circles them: the values repeated because of a dependency — Janeway's address (name → addr), her favourite drink (name → favDrink), Bud's maker (drinkLiked → manf).
nameaddrdrinkLikedmanffavDrink
JanewayVoyagerBudA.B.WickedAle
JanewayVoyagerWickedAlePete'sWickedAle
SpockEnterpriseBudA.B.Bud

Page 124 lists the rules behind those repetitions as "Reasonable FD's to assert":

  1. name -> addr
  2. name -> favdrink
  3. drinkLiked -> manf

Page 123 defines the idea:

Functional dependency — "A column, Y, of the relational table R is said to be functionally dependent upon column X of R if and only if each value of X in R is associated with precisely one value of Y at any given time." It is written R.x → R.y and read "column x functionally determines (identifies) column y". X is the determinant (p. 124).

The slide's definition speaks of single columns, but a determinant can be a set of columns: p. 133 needs "Order_ID, Product_ID" on the left of an arrow. For sets, the definition is a statement about pairs of rows:

\[ t_1[X] = t_2[X] \;\Rightarrow\; t_1[Y] = t_2[Y] \]
\(t_1, t_2\)
any two rows of any legal instance of R
\(t[X]\)
the values of row t in the columns of X

Two consequences matter for everything that follows. First, keys: "If column X is a primary key, then all columns in the relational table R must be functionally dependent upon X" (p. 123). A key value picks out one row, hence one value of every column. Turned around, a set of columns that determines every column is a superkey, and a minimal superkey is what p. 95 calls a key (lesson 8). Textbooks say candidate key, because a table can have several; the primary key is the one the designer picks.

Second, the words "at any given time". A functional dependency is a rule about every legal state of the table, asserted from what the data means — a drinker has one address, a drink has one maker. That is why p. 124 says "reasonable FD's to assert". Rows can refute a rule: one pair that agrees on X and differs on Y is enough. Rows can never prove one. Pine Valley's five rows satisfy 28 single-column dependencies that do not follow from the rules of p. 133 — Unit_Price → Product_ID among them, although nothing stops two products from costing the same.

"Janeway appears twice with Voyager, so name → addr holds." The rows are only consistent with it. It holds because the designer asserts that a drinker has one address. The widget below shows the other side of the coin: rows that agree with a rule nobody asserted.

FD checker: can these rows refute it?

The Drinkers instance of p. 125; every cell can be edited. Type a dependency such as name -> addr (several columns separated by commas) or pick one from the list. The widget looks for two rows that agree on the left side and differ on the right side, and asks separately whether the dependency follows from the three rules asserted on p. 124. The one thing to see: a pair of rows can refute a dependency; no number of rows can prove one.

  1. The field holds addr -> name. Before reading the readouts: do these rows refute it? Does it follow from the three rules? Then press "Add a row" twice.
    What you should see

    Not refuted: Voyager goes with Janeway and Enterprise with Spock. It does not follow from the rules, though — the closure of {addr} is just {addr}. The second added row, Data at Enterprise, refutes it (rows 3 and 5). The three rows never proved it; they had simply not met a counter-example yet.

  2. Pick drinkLiked -> favDrink from the list. Predict the pair of rows that refutes it before you look at the table.
    What you should see

    Rows 1 and 3: both like Bud, and their favourite drinks are WickedAle and Bud. The agreeing cells turn yellow, the disagreeing ones red. One pair is enough to refute a dependency for good.

  3. Pick name -> addr, then change Janeway's address in row 2 to Delta Quadrant. What do the three readouts say together, and what does that mean for the database?
    What you should see

    "Refuted by rows 1 and 2", "it follows", and name → addr in the list of broken rules: the rows now refute a dependency that the rules assert. The instance is illegal, and a database that enforces name → addr must refuse the change. It is also the update anomaly in miniature. Janeway's address was stored twice because name is not the key of Drinkers, and you changed one copy.

  4. Type name, drinkLiked -> manf and read the rules readout. Then type name -> manf.
    What you should see

    The closure of {name, drinkLiked} is all five columns: the pair is a superkey, and neither half is (name gives {name, addr, favDrink}, drinkLiked gives {drinkLiked, manf}), so it is the key of Drinkers. name -> manf does not follow from the rules, and rows 1 and 2 refute it anyway: Janeway likes drinks from two makers.

The table also shows why dependencies produce repetition. name is not the key of Drinkers — Janeway has two rows — so whatever name determines is copied into each of her rows. A determinant that is not a key of its table is the source of every copy in this lesson.

Following the rules: the closure

Page 126, "Basic Idea", is the rule for splitting tables: "To know what FD's hold in a projection, we start with given FD's and find all FD's that follow from given ones. Then, restrict to those FD's that involve only attributes of the projected schema." The slides do not say how to "find all FD's that follow". The standard tool is the attribute closure \(X^+\): start from X and keep adding the right side of every dependency whose left side is already inside, until nothing changes. Then

\[ X \to Y \text{ follows} \iff Y \subseteq X^{+} \]

and X is a superkey exactly when \(X^+\) contains every column. Here is the closure of {name, drinkLiked} under the three rules of p. 124:

  1. Start from the set itself — every set of columns determines itself: {name, drinkLiked}.

  2. name is inside and name → addr: add addr. Now {name, drinkLiked, addr}.

  3. name → favDrink fires as well: add favDrink. Now {name, drinkLiked, addr, favDrink}.

  4. drinkLiked is inside and drinkLiked → manf: add manf. All five columns are in, so no rule can add anything: stop.

  5. Conclusion: {name, drinkLiked} is a superkey. It is also minimal, since the closure of {name} is {name, addr, favDrink} and the closure of {drinkLiked} is {drinkLiked, manf}. So {name, drinkLiked} is the key of Drinkers.

Now the projection rule works. Split Drinkers into (name, addr, favDrink), (drinkLiked, manf) and (name, drinkLiked). The dependencies that hold in each piece are those of the closure that use only its columns: name → addr, favDrink in the first, drinkLiked → manf in the second, and only trivial ones in the third, whose key is the pair itself. Section 4 explains why this is the right split: name → addr hangs on half of the key.

1NF: first, be a relation

Before the invoice of p. 130 there is the invoice of p. 129, titled "Multivalued attributes". It is the same two orders written the way a paper invoice looks: order 1006 appears once, with three product lines stacked inside it. Fig. 1 shows three of its ten columns before and after.

Order_ID Customer_ID Product_ID 1006 2 7 5 4 1007 6 11 4 one cell, a set of values

Before — p. 129: "NOT a relation"

Order_ID Customer_ID Product_ID 100627 100625 100624 1007611 100764 copies of one fact

After — p. 130: a relation, "not a well-structured one"

Fig. 1. The same two orders, three of the ten columns. Left: one row per order, so a single Product_ID cell holds a set of values — a repeating group. Right: one row per order line; every cell holds one value, and the key is the pair (Order_ID, Product_ID). The price is visible already: the customer of each order is repeated on every one of its lines (highlighted).

Page 128 states the rules of the first normal form:

The last line of p. 128 explains the red verdict of p. 129: "All relations are in 1st Normal Form". A relation is a set of tuples that take one value from each domain (lesson 8), so a table with a set inside a cell is not in 1NF, and therefore "NOT a relation" at all. 1NF is not a quality grade. It is the entry ticket to the relational model.

It is also all that p. 130 has. The flat invoice is a legal relation with a legal key, and it still holds the 15 copies of section 1. Removing repeating groups removes nested lists, not repeated facts. That second job needs the dependencies.

Keep Fig. 1 in mind for later. A MongoDB order document with an embedded array of its lines is the left-hand picture, written on purpose: document stores give up 1NF deliberately (lesson 13).

2NF and 3NF: the whole key, and nothing but the key

Page 133 draws the dependencies of the 1NF invoice over its ten columns and lists them in red:

and concludes in blue: "Therefore, NOT in 2nd Normal Form". The key is the pair (Order_ID, Product_ID). Look at where each arrow starts: at the whole key, at half of it, or at a column outside it. That one question sorts every dependency into three kinds.

Full dependency — a non-key column depends on the whole key and on no part of it. Only Ordered_Quantity: an amount belongs to one product on one order.

Partial dependency — a non-key column depends on part of a composite key. Order_Date depends on Order_ID alone, Unit_Price on Product_ID alone.

Transitive dependency — a non-key column depends on the key only through another non-key column: the key determines Customer_ID, which determines Customer_Name.

composite primary key Order_ID Product_ID Ordered_Quantity Unit_Price full partial half the key is enough

Partial: Product_ID → Unit_Price

Order_ID Customer_ID Customer_Name key non-key a fact about the customer

Transitive: Order_ID → Customer_ID → Customer_Name

Fig. 2. Where the arrow starts decides the kind. Left: Unit_Price hangs on half of the composite key — a partial dependency, which 2NF removes; Ordered_Quantity needs both halves. Right: Customer_Name depends on the key only through the non-key Customer_ID — a transitive dependency, which 3NF removes.

The two normal forms forbid one kind each. Second normal form, p. 132: "1NF PLUS every non-key attribute is fully functionally dependent on the ENTIRE primary key" — "No partial functional dependencies". Page 131 adds that 2NF "deals with relationships between composite key columns and non-key columns". So a table whose key is a single column is in 2NF as soon as it is in 1NF: there is no "part of the key" for a column to hang on. (The fine print for a table with a second, composite candidate key is in section 5.)

Third normal form, p. 136: "2NF plus no transitive dependencies (functional dependencies on non-primary-key attributes)". The slide explains the name: "this is called transitive, because the primary key is a determinant for another attribute, which in turn is a determinant for a third". Page 135 says it shorter: 3NF "requires that all columns depend directly on the primary key".

The repair is the same both times. Page 131 moves the offending columns into new tables and says how to tie them back: "Create relationships between these new tables and their predecessors through the use of foreign keys"; p. 136 spells the recipe out for 3NF: "non-key determinant with transitive dependencies go into a new table; non-key determinant becomes primary key in the new table and stays as foreign key in the old table". Read the same sentence with "part of the key" in place of "non-key determinant" and you have the 2NF step.

Each arrow that does not start at the whole key becomes a table of its own: its determinant is the new key and stays behind as a foreign key.

Run the recipe on the invoice yourself. Draw the dependencies of the 1NF table, then let the widget split it as pp. 134 and 137 do, and test the three anomalies at each step.

Normalization lab: the Pine Valley invoice, 1NF to 3NF

The ten columns of the invoice stand vertically, the slide's strip of p. 133 turned on its side; key columns are shaded and underlined. Tap a column to start an arrow, then tap the column it determines; for the whole key, tap Order_ID and then Product_ID. With a mouse you can also drag from column to column. Partial dependencies are drawn on the left, full and transitive ones on the right. "Next" splits the table, and each arrow is then labelled with the table it became. The data tables underneath show the rows; the anomaly test marks what a change touches. The one thing to see: where an arrow starts decides its kind, and every arrow that does not start at the whole key becomes a table of its own.

  1. Predict first: is Customer_ID → Customer_Name partial, full or transitive? Tap Customer_ID, then Customer_Name, and read the note.
    What you should see

    Transitive. Customer_ID is not part of the key (Order_ID, Product_ID); the key reaches Customer_Name only through it. The arrow appears on the right with the label "transitive", not on the left with the partial ones. Press Next: at 2NF this arrow is still there, inside CUSTOMER_ORDER, as on p. 134. Only the third step turns it into a table.

  2. Now try two rules that are not rules. Predict which two rows refute Product_ID → Order_Date; then try Unit_Price → Product_ID.
    What you should see

    Rows 3 and 5: product 4 is on order 1006 (10/24/2004) and on order 1007 (10/25/2004). Unit_Price → Product_ID is refused too, but these rows do not refute it — each price occurs with one product. It is refused because the four dependencies of p. 133 do not imply it: a second product at 650.00 would break it. The widget takes the rules from the meaning, not from the rows — and so must you when you answer a question about dependencies.

  3. Choose the anomaly test "Value Furniture moves to a new address" and step through 1NF, 2NF and 3NF. Predict the three counts; then switch on order 1008 and predict again.
    What you should see

    3, 1, 1 on the slide's rows: 2NF already looks perfect, but only because each customer has placed a single order. With order 1008 the counts are 4, 2, 1. CUSTOMER_ORDER stores the address once per order; only 3NF stores it once per customer. That is what removing the transitive dependency buys.

  4. Run "Add product 12, which nobody has ordered yet" and "Register a customer who has not ordered yet". Predict the step at which each first becomes possible, and name the arrow responsible.
    What you should see

    The product: from 2NF on. Its trouble came from the partial dependency on Product_ID, and PRODUCT is keyed by Product_ID alone. The customer: only at 3NF. Its trouble came from the transitive dependency on Customer_ID; as long as customer data lives in a table keyed by Order_ID, a customer exists only through an order. Each anomaly disappears exactly when its arrow becomes a table.

The result is the slides' two pictures. After the first split, p. 134 shows ORDER_LINE (3NF), PRODUCT (3NF) and CUSTOMER_ORDER (2NF), with the caption "Partial Dependencies are removed, but there are still transitive dependencies". After the second, p. 137 shows ORDER (3NF), whose Customer_ID has a dashed underline, and CUSTOMER (3NF): "Transitive dependencies are removed". Four tables, every fact once:

Underlined columns are primary keys; a star marks a foreign key, as in the mapping slides of lesson 8.

Why must Customer_ID stay in ORDER?

Without it, nothing would say which customer placed order 1006: joining ORDER with CUSTOMER could not rebuild the invoice, and information would be lost. The shared column has to be the key of one side — here Customer_ID is the key of CUSTOMER — so the join returns exactly the original rows. Textbooks call this a lossless-join decomposition; the slides' recipe guarantees it by keeping the determinant as a foreign key.

"Customer_ID → Customer_Name is a partial dependency, because Customer_ID is an ID." Partial means the determinant is part of the key, and Customer_ID is not part of (Order_ID, Product_ID). It is a non-key column, so the dependency is transitive — and that is why 2NF leaves it alone.

The slides' second 3NF example, on p. 135, is a publisher with its address:

BCNF and the ladder: where the course stops

Page 138 draws normalization as a ladder. Each rung is the one above with one more kind of trouble removed, so the forms nest: every BCNF table is in 3NF, every 3NF table in 2NF, every 2NF table in 1NF.

Table with multivalued attributes First normal form Second normal form Third normal form Boyce–Codd NF Fourth normal form Fifth normal form Remove multivalued attributes Remove partial dependencies Remove transitive dependencies Remove remaining anomalies resulting from functional dependencies Remove multivalued dependencies Remove remaining anomalies = "3.5NF", p. 139 beyond this course
Fig. 3. "Steps in Normalization", p. 138, redrawn with the slide's wording. The course covers the rungs down to Boyce–Codd normal form — "(we cover 3.5)", p. 120 — and stops before the two forms that deal with multivalued and join dependencies (dashed).

The rung the course does cover last is defined on p. 139: "Boyce–Codd normal form (or BCNF or 3.5NF) is a slightly stronger version of the 3NF."

Boyce–Codd normal form — "For every one of its dependencies X → Y, at least one of the following conditions hold: X → Y is a trivial functional dependency (Y ⊆ X); X is a superkey for schema R" (p. 139).

In words: in BCNF every determinant is a superkey. That is "nothing but the key" applied to every arrow, without exceptions. Check it on the four Pine Valley tables: each has one arrow, starting at its own key, so all four are in BCNF as well as in 3NF.

Where does "slightly" come from? The general textbook definition of 3NF has an escape clause: for every non-trivial X → A, either X is a superkey or A belongs to some candidate key. BCNF drops the clause. The two differ only in tables with two or more candidate keys that overlap — none of the slides' examples. The standard one, from the textbooks, is a small teaching table:

TEACHING(Student, Course, Teacher): each student has one teacher per course, and each teacher teaches one course. Candidate keys: (Student, Course) and (Student, Teacher). Highlighted: "Rossi teaches Databases", stored twice.
StudentCourseTeacher
AnnaDatabasesRossi
BrunoDatabasesRossi
CarlaDatabasesVerdi
AnnaStatisticsBianchi

Teacher → Course holds, and Teacher is not a superkey: BCNF fails, and the repetition is real. Yet Course is part of the candidate key (Student, Course), so the escape clause saves 3NF. Splitting into TEACHES(Teacher, Course) and FOLLOWS(Student, Teacher) removes the repetition, but the rule "one teacher per student per course" can no longer be checked inside a single table. Insert (Anna, Verdi) into FOLLOWS: both tables accept it, and only their join reveals that Anna now has two Databases teachers. That is why textbooks name 3NF, always reachable without losing a dependency, as the practical target — the target p. 121 sets.

"In 3NF, no column may depend on a non-key column." The slides phrase 2NF and 3NF with "the primary key", which is exact only when a table has a single candidate key. Take EMPLOYEE(Emp_ID, Tax_Code, Name) with primary key Emp_ID, where every tax code belongs to one employee. Name depends on Tax_Code, a column that is not the primary key — yet there is no repetition at all, because Tax_Code is a candidate key and therefore a superkey. Read "key" as "any candidate key" and "non-key" as "part of no candidate key", and the table is in BCNF. The same reading fixes 2NF: "a single-column key means 2NF" holds when every candidate key is a single column. At the exam, write the slides' definitions; the refinement matters only for a table with more than one candidate key.

The last two rungs leave functional dependencies behind. Fourth normal form removes multivalued dependencies: two independent lists stored in one table, such as an employee's skills and languages, which force every skill to be paired with every language. Fifth normal form handles the rarer tables that can be rebuilt without loss only by joining three or more of their projections. Neither can be stated with the arrows of this lesson, and the course stops at "3.5" (p. 120).

BCNF was developed in 1974 by Raymond F. Boyce and Edgar F. Codd. Boyce had co-developed SEQUEL — the language later renamed SQL — with Don Chamberlin. He died of an aneurysm in June 1974, aged 27.

Two routes to the same tables — and why NoSQL leaves them

Back to the puzzle. What was wrong with five rows that contained no wrong value? Their structure: facts about three different things — orders, customers, products — shared one row, glued together by arrows that did not start at the whole key. Each trouble traces back to an arrow. The three-cell move: the address hangs on Order_ID, half of the key, through Customer_ID, a non-key column — the 2NF split cut it to one copy per order, the 3NF split to one per customer. The product that could not be added and the dresser that vanished with its order line: both hang on Product_ID, the other half of the key, and PRODUCT cured both at once. Every arrow became a table, and every fact now sits in one cell.

There was a second route to the same place. Draw the ER diagram behind the invoice and translate it with the recipe of lesson 8, and the four tables appear directly, with no table ever holding a repeated fact.

Customer Customer_ID Customer_Name Customer_Address places (0,N) (1,1) Order Order_ID Order_Date contains (1,N) (0,N) Ordered_Quantity Product Product_ID Product_Description Product_Finish Unit_Price

Route 1: the ER diagram, translated

CUSTOMER Customer_ID Customer_Name Customer_Address ORDER Order_ID Order_Date Customer_ID* ORDER_LINE Order_ID* Product_ID* Ordered_Quantity PRODUCT Product_ID Product_Description Product_Finish Unit_Price the tables of pp. 134–137

Route 2: the 3NF tables of the split

Fig. 4. Two routes, one destination. Left: an ER diagram for the invoice, not on the slides — a customer places 0 to N orders, an order is placed by exactly one customer and contains 1 to N products, a product is in 0 to N orders. With lesson 8's recipe, "places", a (0,N)–(1,1) relationship, posts Customer_ID into ORDER; "contains", a (1,N)–(0,N) relationship, becomes a separate table with a composite key and the relationship attribute inside. Right: the four tables — exactly the ones normalization reached on pp. 134–137.

So the two halves of the deck meet. A correct ER diagram, translated by the rules, gives 3NF tables. Normalization is the check, and the repair, for tables that did not come from a good diagram: a spreadsheet, a CSV export, or the Employee table of p. 117 with its DeptName repeated on every employee (lesson 8).

The split has a price, and you pay it on every read. To see one invoice line again, the database must join the four tables back together:

-- returns the five invoice lines of p. 130
-- ("ORDER" is quoted: ORDER is an SQL keyword)
SELECT l.Order_ID, o.Order_Date, c.Customer_Name, c.Customer_Address,
       p.Product_Description, p.Unit_Price, l.Ordered_Quantity
FROM ORDER_LINE l
JOIN "ORDER" o  ON o.Order_ID = l.Order_ID
JOIN CUSTOMER c ON c.Customer_ID = o.Customer_ID
JOIN PRODUCT p  ON p.Product_ID = l.Product_ID;

The result is a wide table like the data frames statistical models are fitted on: one row per observation, with every fact repeated where it applies. On one machine three joins are cheap. Across a cluster, where ORDER_LINE and CUSTOMER may live on different nodes, every join is network traffic (lesson 3, lesson 5). That is the main reason the NoSQL half of the course denormalises on purpose. MongoDB embeds the lines inside the order document — the left side of Fig. 1, chosen deliberately (lesson 13). Cassandra builds one table per query and copies the same data into each of them (lessons 20 and 21); the Cassandra exercise session sums it up on p. 1551: "No joins or foreign keys. Data is denormalized and duplicated for query efficiency." Those systems accept the update anomaly in exchange for reads without joins.

The normal forms do not stop being useful there. They are the yardstick that tells you exactly which anomalies you bought, and which application code must now keep the copies in step.

Exam-style questions

The practical part is about Neo4j, MongoDB, Elasticsearch, Redis and SparkQL, and no past paper asks for a decomposition there, so normalization belongs to the theory part: open questions, multiple-choice and true-or-false questions, where a wrong answer may carry a penalty (announced just before the exam). The restaurant exercise trains the same skill on the ER scenario of a past paper.

Define a functional dependency X → Y on a relational table R. Explain why the primary key of R functionally determines every column of R, and why the rows stored in R can show that a functional dependency does not hold but can never show that it holds.

Show model answer

Y is functionally dependent on X if each value of X in R is associated with precisely one value of Y at any given time: any two rows that agree on X also agree on Y. X is called the determinant. A primary key identifies exactly one row, so each key value is associated with one value of every column, and every column depends on the key; conversely, a set of columns that determines all columns is a superkey. A functional dependency is a constraint on every legal state of the table, asserted by the designer from the meaning of the data — for example "each drinker has one address". One pair of rows with the same X and different Y refutes it; rows that agree with it only show that the current instance is consistent with it, since a later row may break it.

A complete answer has three parts: the definition (the slides' "precisely one value of Y at any given time", or the two-row form), the key argument, and the instance-versus-rule distinction. An answer that "checks the FD on the data" misses the third.

Consider the table Employee(EmpID, EmpName, Payrate, Hours Worked, Dept#, DeptName) with primary key EmpID, where every employee works in one department and every department has one name. In which normal form is the table?

  • It is not in 1NF Every cell holds one value and the rows have a key: it is a relation, hence in 1NF.
  • In 1NF but not in 2NF A partial dependency needs a composite key; EmpID is a single column, so there is no part of the key to depend on.
  • In 2NF but not in 3NF EmpID → Dept# → DeptName is a transitive dependency through the non-key Dept#: DeptName is a fact about the department, repeated for every employee.
  • In 3NF 3NF forbids transitive dependencies, and DeptName depends on the key only through Dept#.
Show model answer

In 2NF but not in 3NF. The single-column key rules out partial dependencies, but EmpID → Dept# and Dept# → DeptName make DeptName transitively dependent on the key. Splitting off Department(Dept#, DeptName) and keeping Dept# in Employee as a foreign key gives 3NF.

This is the table of p. 117 ("Fixing One Fact Multiple Places", lesson 8): the two halves of the deck ask the same question. The trap is option 2 — "a fact in the wrong place" sounds partial, but with a one-column key nothing can be partial.

Which of the following statements are true? Select all that apply.

  • Every table in BCNF is also in 3NF. BCNF requires every determinant to be a superkey; 3NF accepts that, and more.
  • Every table in 3NF is also in BCNF. False: TEACHING(Student, Course, Teacher) with Teacher → Course is in 3NF, because Course belongs to a candidate key, but not in BCNF, because Teacher is not a superkey.
  • A table in 1NF whose only candidate key is a single column is in 2NF. Partial dependencies need a part of a composite key; with one key column there is none. (The slides say "primary key"; "only candidate key" makes the statement exact.)
  • A table whose cells hold lists of values is in 1NF as long as it has a primary key. False: 1NF requires atomic values — "no sets of values within a column" (p. 128); such a table is "NOT a relation" (p. 129).
  • A transitive dependency is a dependency of a non-key column on part of a composite primary key. False: that is a partial dependency. A transitive dependency goes through another non-key column.
Show model answer

True: "Every table in BCNF is also in 3NF" and "A table in 1NF whose only candidate key is a single column is in 2NF". The other three are false: 3NF does not imply BCNF; a table with lists in its cells is not in 1NF; the definition given is that of a partial dependency.

A select-all question where a wrong tick may be penalised: tick only what you can justify in one line. The pair "partial versus transitive" is the most frequent confusion of this topic.

What are the goals of normalization? Using a table that stores orders, customers and products in the same rows, explain the update, insertion and deletion anomalies that normalization removes.

Show model answer

Normalization aims at a set of relational tables that are free of redundant data and can be consistently and correctly modified: it eliminates redundant data and ensures that each table only stores related data, which saves space and makes the data easier to maintain; the target is 3NF. In INVOICE(Order_ID, Product_ID, Order_Date, Customer_ID, Customer_Name, Customer_Address, Product_Description, Product_Finish, Unit_Price, Ordered_Quantity) with key (Order_ID, Product_ID): update anomaly — the customer's address is repeated on every line of every order, so a change of address must be applied to many rows or the table becomes inconsistent; insertion anomaly — a new product that nobody has ordered cannot be inserted, because Order_ID is part of the key and cannot be NULL; deletion anomaly — deleting the only order line of a product also deletes the product's description and price. Splitting the table into ORDER_LINE, PRODUCT, ORDER and CUSTOMER stores each fact once and removes all three.

The slides give the goals (pp. 121, 127) but not the anomaly names, which come from the textbooks; a complete answer gives both the goal sentence and one concrete example per anomaly. The Pine Valley invoice is the natural example to use.

Consider the restaurant ER diagram of the exam of 6 Sep 2024: Waiter (a Person, key Personal_ID, with Surname and Hiring Date) is related by Serve (1:N on both sides) to Table (key Table_ID, with Max Seats), and Table is related by Has (Table 1:1, Restaurant 1:N) to Restaurant (key Restaurant_ID, with Name). The Serve relationship has been exported to a single table:

SERVICE(Personal_ID, Table_ID, Surname, Hiring_Date, Max_Seats, Restaurant_ID, Restaurant_Name), primary key (Personal_ID, Table_ID).

(a) List the functional dependencies that follow from the ER diagram and classify each as full, partial or transitive. (b) In which normal form is SERVICE? (c) Decompose it into 3NF, marking primary and foreign keys.

Show model answer

(a) Personal_ID → Surname, Hiring_Date: partial (part of the key). Table_ID → Max_Seats, Restaurant_ID: partial; Restaurant_ID depends on Table_ID because every table belongs to exactly one restaurant. Restaurant_ID → Restaurant_Name: transitive, since the key reaches Restaurant_Name only through the non-key Restaurant_ID. No non-key column depends fully on the whole key: Serve has no attributes.

(b) 1NF (atomic values, a key), not 2NF (partial dependencies).

(c) 2NF: WAITER(Personal_ID, Surname, Hiring_Date), TABLE(Table_ID, Max_Seats, Restaurant_ID, Restaurant_Name), SERVE(Personal_ID*, Table_ID*). TABLE still has a transitive dependency, so for 3NF split it: TABLE(Table_ID, Max_Seats, Restaurant_ID*) and RESTAURANT(Restaurant_ID, Restaurant_Name). Final schema: WAITER, SERVE, TABLE, RESTAURANT; underlined = primary key, * = foreign key.

The scenario is the restaurant paper's ER; the table is made up for this lesson. A useful self-check: the result equals what the ER-to-table recipe of lesson 8 gives directly — Serve, many-to-many, becomes a separate table with a composite key; Has, with Table on the (1,1) side, posts Restaurant_ID into TABLE.

State the definition of Boyce–Codd normal form. How does it differ from third normal form, and why do the lecture slides call it "3.5NF"? Give an example of a table that is in 3NF but not in BCNF.

Show model answer

A table R is in BCNF if, for every functional dependency X → Y, either the dependency is trivial (Y ⊆ X) or X is a superkey of R. 3NF requires, for every non-trivial X → A, that X is a superkey or that A belongs to some candidate key; BCNF drops this second alternative, so it is slightly stronger, and every BCNF table is in 3NF — hence "3.5NF", between 3NF and 4NF. The two differ only when a table has overlapping candidate keys. Example: TEACHING(Student, Course, Teacher) with {Student, Course} → Teacher and Teacher → Course; the candidate keys are {Student, Course} and {Student, Teacher}. Teacher → Course violates BCNF because Teacher is not a superkey, but satisfies 3NF because Course is part of a candidate key. Decomposing into (Teacher, Course) and (Student, Teacher) reaches BCNF but loses the dependency {Student, Course} → Teacher, which is why 3NF is often the practical target.

The definition is on p. 139 almost word for word; the example and the dependency-preservation argument come from the textbooks the slides cite on p. 140. The example shows that you understand the difference instead of reciting it.

Check yourself

A table holds 10,000 rows, and any two of them with the same value of X also have the same value of Y. What can you conclude about X → Y?

  • It holds, because 10,000 rows are a large sample Size does not help: a dependency is a rule about every legal state, and the next row may break it.
  • It does not hold Nothing refutes it: no pair agrees on X and differs on Y.
  • Only that this instance does not refute it Rows can refute a dependency, never prove it; whether it holds is decided by the meaning of the data.
  • X is a key of the table A key must determine every column, and X → Y says nothing about the others.

In the 1NF invoice with key (Order_ID, Product_ID), what kind of dependency is Customer_ID → Customer_Name?

  • Partial Partial would need Customer_ID to be part of the key; it is not.
  • Transitive The key determines Customer_ID, a non-key column, which determines Customer_Name.
  • Full Full dependencies start at the whole key; this one starts at a non-key column.
  • Trivial Trivial means the right side is part of the left side; Customer_Name is not part of Customer_ID.

A table is in 1NF, and its only candidate key is a single column. Which statement is then guaranteed?

  • It is in 2NF A partial dependency needs a proper part of the key, and a single column has none.
  • It is in 3NF Transitive dependencies are still possible, like DeptName through Dept# in the Employee table of p. 117.
  • It is in BCNF BCNF implies 3NF, which is not guaranteed either.
  • It has no redundancy A transitive dependency still repeats facts.

On the slide's five rows, which troubles of the 1NF invoice are already gone after the 2NF split of p. 134? Select all that apply.

  • Adding a product that nobody has ordered PRODUCT is keyed by Product_ID alone, so the product needs no order.
  • Losing product 11 when the line (1007, 11) is cancelled Only the ORDER_LINE row goes; PRODUCT keeps product 11.
  • Registering a customer who has not ordered yet Customer data still lives in CUSTOMER_ORDER, keyed by Order_ID: this needs 3NF.
  • Losing Furniture Gallery when order 1007 is deleted The customer's only CUSTOMER_ORDER row goes with the order: also a 3NF matter.

The rule behind it: an anomaly disappears at the step that turns its arrow into a table — partial arrows at 2NF, the transitive arrow at 3NF.

Why does the 3NF split keep Customer_ID in ORDER instead of moving it entirely into CUSTOMER?

  • Because Customer_ID is part of the key of ORDER The key of ORDER is Order_ID alone; Customer_ID is a non-key column there.
  • Because it is the foreign key that says which customer placed each order, so the join can rebuild the original rows The recipe of p. 136: the determinant "stays as foreign key in the old table". Without it the decomposition loses information.
  • Because removing it would break 2NF ORDER would still be in 2NF without it; the problem is that the link to the customer would be lost.

A table is in 3NF but not in BCNF. What must be true of it?

  • It has a partial dependency Then it would not even be in 2NF.
  • Some dependency X → A has a determinant X that is not a superkey, and A is part of a candidate key That is exactly the escape clause of 3NF that BCNF removes; it needs overlapping candidate keys, as in TEACHING.
  • It has a repeating group Then it would not be in 1NF.
  • Its primary key is a single column and it has no other candidate key With a single candidate key, 3NF and BCNF coincide.

A MongoDB collection stores each order as one document with an array of its lines. In the slides' terms, this document is…

  • a relation in 3NF An array inside a field is a set of values within a column: not even 1NF.
  • the repeating group of p. 129, chosen on purpose to read an order without joins Document stores give up 1NF deliberately: one read returns the whole order. If the lines also copy product data, the duplication and its update anomalies come with it.
  • an error that MongoDB rejects MongoDB accepts and encourages nested arrays; the relational model is the one that rejects them.

Key take-aways

What to watch and read