Bad design, functional dependencies and closure
You learn why putting everything into one table produces anomalies, and then functional dependencies, Armstrong's axioms and finding keys with closure. All normalization questions rely on these calculations.
This is the first week of the normalization section. The most reliable points after the midterm come from here, because most of the questions are mechanical: compute a closure, find a key, derive an FD. The three skills on this page are the basis of the next two weeks (normal forms, decomposition):
- Explaining why one big table causes problems with anomalies.
- Understanding what a functional dependency means and checking whether it holds in the sample data.
- Finding superkeys and candidate keys with attribute closure.
Bad design: everything in one table
In the university database we use all semester, takes, student, course and department are separate tables. Imagine you merged them into one “wide” table:
CREATE TABLE wide AS
SELECT t.stu_id, s.name AS stu_name, t.course_id, c.title, c.credits,
c.dept_name AS course_dept, d.building, t.grade
FROM takes t JOIN student s ON s.stu_id = t.stu_id
JOIN course c ON c.course_id = t.course_id
JOIN department d ON d.dept_name = c.dept_name;
The result is 14 rows (I am not showing the stu_name and credits columns to save space):
| stu_id | course_id | title | course_dept | building | grade |
|---|---|---|---|---|---|
| S1 | CS101 | Intro to Programming | CS | Galata | A |
| S2 | CS101 | Intro to Programming | CS | Galata | B |
| S3 | CS101 | Intro to Programming | CS | Galata | NULL |
| S5 | CS101 | Intro to Programming | CS | Galata | F |
| S1 | CS201 | Data Structures | CS | Galata | B |
| S2 | CS201 | Data Structures | CS | Galata | C |
| S1 | CS305 | Databases | CS | Galata | A |
| S4 | CS305 | Databases | CS | Galata | B |
| S3 | EE210 | Circuits | EE | Galata | A |
| S1 | MA101 | Calculus | MATH | Rumeli | B |
| S3 | MA101 | Calculus | MATH | Rumeli | C |
| S4 | MA101 | Calculus | MATH | Rumeli | A |
| S4 | MA202 | Linear Algebra | MATH | Rumeli | A |
| S7 | MA202 | Linear Algebra | MATH | Rumeli | B |
Writing queries became easier, but the table is open to three kinds of errors:
| Anomaly | How it happens in this table |
|---|---|
| Update anomaly | If the CS department moves to another building, you have to change 8 rows. If you forget one, two different buildings appear for CS and the data is contradictory. |
| Insertion anomaly | You cannot add the course BI100 (Biology) while no student takes it. In the same way, S6 (Firat), who has taken no course, is not in the table. |
| Deletion anomaly | EE210 is in only 1 row. If S3 drops the course and the row is deleted, the information “EE210 = Circuits, 3 credits” is lost too. |
The root cause: the table squeezes independent facts (the name of a course, the building of a department, a student’s grade) into the same row. The way to express these facts formally is a functional dependency.
Functional dependency
Let and be sets of attributes of the schema . means: any two rows with the same values also have the same values. It is read ” determines ”.
Real-world rules for the wide table:
From these FDs, {stu_id, course_id} comes out as the only candidate key of the table (you will see below how to find it).
Checking on sample data
A single pair is enough to violate an FD: two rows with the same left side and a different right side. With SQL you find it by grouping:
-- does building -> course_dept hold?
SELECT building FROM wide
GROUP BY building
HAVING COUNT(DISTINCT course_dept) > 1;
Galata
Galata matches both CS and EE: is violated. When you run the same query for course_id/title or course_dept/building, the result is empty, so these FDs hold in this instance. For credits -> course_dept the query returns the values 3 and 4 (4-credit courses are in both CS and MATH).
PITFALL · sample data is not proof. If the query returned empty, it means the FD is not violated in these rows, not that it is valid in the schema. A row that arrives tomorrow can break it. An FD is a design decision (a real-world rule); data can only refute it.
Trivial dependency
If , then is true in every relation: , . They carry no information. If the exam says “list the non-trivial FDs”, do not write these.
Armstrong’s axioms
Other FDs can be derived from a given set of FDs . The set of everything that can be derived is called the closure of F and is written . Three basic rules are enough (sound and complete):
| Rule | Statement |
|---|---|
| Reflexivity | If then |
| Augmentation | If then |
| Transitivity | If and then |
Practical rules derived from these:
| Rule | Statement |
|---|---|
| Union | If and then |
| Decomposition | If then and |
| Pseudotransitivity | If and then |
Worked example. Given , derive .
1. A -> B, B -> C transitivity A -> C
2. A -> C, add D augmentation AD -> CD
3. AD -> CD, CD -> E transitivity AD -> E
PITFALL · you cannot split the left side. The decomposition rule holds only for the right side. From you do not get . When you see “decomposition”, remember that you are splitting the right side.
Since is usually very large, it is not listed by hand. In practice the real tool is the closure of an attribute set.
Attribute closure
: the set of all attributes that determines under . The algorithm:
result := X
repeat:
for each L -> M in F:
if L ⊆ result then result := result ∪ M
until there is no change
Worked example. , .
{A}+ : A
A -> B AB
B -> E ABE
(no C for AC -> D, no D for DE -> G) → {A,B,E}
{A,C}+ : AC
A -> B ABC
AC -> D ABCD
B -> E ABCDE
DE -> G ABCDEG → the whole schema
Closure answers three questions at once:
- Can be derived? Yes if . , so cannot be derived but can.
- Is a superkey? Yes if . AC is a superkey.
- Is a candidate key? Yes if it is a superkey and none of its proper subsets is a superkey. and do not give the schema, so AC is a candidate key.
Finding all candidate keys
Brute force means subsets. In the exam the quick way is this observation:
- An attribute that appears on the right side of no FD cannot be derived from anything else, so it must be in every key.
- An attribute that appears only on the right side (never on the left) is in no minimal key.
- First take the closure of the “mandatory” set. If it is not enough, add one attribute at a time, then two. Skip the supersets of a key you have found.
Worked example. , .
Never on a right side: B → B is in every key
{B}+ = {B} → not enough
{A,B}+: AB -> C, C -> D, D -> E = ABCDE key
{B,C}+: C -> D, D -> E, E -> A, AB -> C = ABCDE key
{B,D}+: D -> E, E -> A, AB -> C = ABCDE key
{B,E}+: E -> A, AB -> C, C -> D = ABCDE key
Four candidate keys: AB, BC, BD, BE. You do not need to try the 3-sets, since all of them contain one of these.
An example with a cycle: , . D is on no right side, . Any one of A, B or C completes the cycle: the keys are AD, BD, CD.
TIP · the number of superkeys. If there is a single candidate key with attributes, the number of superkeys in a schema with attributes is (the key plus any subset of the rest). with key A: superkeys. If there are several keys, be careful not to double count their intersections.
Back to the wide table, write it with letters: S = stu_id, N = stu_name, C = course_id, T = title, K = credits, D = course_dept, B = building, G = grade. . S and C are on no right side, and gives everything: the only candidate key is {stu_id, course_id}. : only a part of the key determines this many attributes. Next week we will give this a name: partial dependency.
Common mistakes
| Wrong idea | The truth |
|---|---|
| If there is no violation in the sample data, the FD is valid | Data only refutes; an FD is a design rule |
| If then | The left side is not split; only the right side decomposes |
| One pass over the FDs is enough in closure | Scan again until no change; a newly added attribute can unlock an old FD |
| Superkey = candidate key | A candidate key is a minimal superkey |
| There is only one candidate key | There can be several (AB, BC, BD, BE) |
| Skipping the attribute that is on no right side | That attribute must be in every key |
COUNT(DISTINCT y) also counts NULL | It does not count NULLs; check a right side that has NULLs separately |
Test yourself
Exam-style questions. Solve them on paper first, then reveal the answer.
In the wide table wide(stu_id, stu_name, course_id, title, credits, course_dept, building, grade), student S3 drops EE210 and we delete that row. Afterwards the database no longer knows that EE210 is called *Circuits* and has 3 credits. Which anomaly is this?
Show answer
EE210 appears in only 1 row of the wide table (only S3 took it). When the record is deleted, the course's own information (title, credits) goes with it. Losing information you did not want to lose together with a deletion is a deletion anomaly.
Is the functional dependency trivial? Explain in one sentence.
Show answer
is trivial if . Here , so it always holds in every relation. It carries no information.
The department table contains (CS, Galata, 500000), (EE, Galata, 350000), (MATH, Rumeli, 200000), (BIO, Kadikoy, 150000). Does the FD building -> dept_name hold on this instance?
Show answer
To violate an FD, two rows with the same left-side value and different right sides are enough. Galata matches both CS and EE. Conversely, dept_name -> building holds in this instance, but an instance cannot prove that an FD is valid in the schema, it can only refute it.
Write an SQL query that returns every course_id that violates the FD course_id -> title in table wide. (An empty result means the FD holds on this instance.)
Show answer
Group by the left side and count how many distinct values the right side has in each group. If it is more than 1, there is a violation. On the sample data the result is empty. Note: COUNT(DISTINCT ...) does not count NULLs, so check separately if the right side can be NULL.
Let with . Compute .
Show answer
Start: {A}. : {A,B}. : {A,B,E}. For we need C, which we do not have. For we need D, which we do not have. No more changes: .
Same and as above. Is a superkey of ? Is it a candidate key?
Show answer
: with we get {A,B,C}, with {A,B,C,D}, with {A,B,C,D,E}, with everything. It is a superkey. A and C are on the right side of no FD, so they must be in every key. and do not give the whole schema, so AC is minimal and the only candidate key.
Let with . Is a superkey?
Show answer
Start with {B,D}. adds nothing new. needs A, needs C, needs E. We have none of them. The closure stays at {B,D}, not the whole schema.
Same and as the previous question. Find all candidate keys.
Show answer
B is on no right side, so it is in every key. {B}+ = {B,D}, not enough. Add one attribute to B:
AB: with , , we get everything. A key.
BC: with , , we get everything. A key.
BE: with , , we get everything. A key.
BD: {B,D}, not a key. Supersets of AB, BC, BE are not minimal; try the 3-sets that contain BD: ABD, BCD, BDE already contain a key. Result: AB, BC, BE.
Let with . Does imply ?
Show answer
The answer to whether can be derived is: is ? , and D is not in it. So does not follow from F. (But does follow, because contains everything.)
Given , derive using Armstrong's axioms. Name each rule.
Show answer
1) and , transitivity: .
2) Add D to both sides, augmentation: .
3) and , transitivity: .
Check: , and E is in it.
Let with . List all candidate keys and give the number of superkeys.
Show answer
C and D are on no right side: they are in every key. {C,D}+ = {C,D}. Add A: {A,C,D}+ = everything. Add B: {B,C,D}+ = everything. Both are minimal. The superkeys are the sets that contain CD and at least one of A or B: ACD, BCD, ABCD. Total 3.
The instructor table has (I1, Aksoy, CS, 95000) and (I2, Demir, CS, 80000). A student claims dept_name -> salary because 'every department has a fixed salary scale'. Does the instance support this FD?
Show answer
For the same dept_name (CS) there are two different salaries, so the FD is violated in this instance. What makes an FD valid is a real-world rule; instance data can only refute it. Here it is refuted. MATH violates it too, with two different salaries.
Verification: The wide table and FD violation checks were run on the sample database with node:sqlite; all closure, key and derivation answers were computed with the verify/SEN2104/lib.mjs functions (verify/SEN2104/09.mjs, 39 checks, 0 errors).