Decomposition: lossless join, dependency preservation, BCNF and 3NF algorithms
You learn to split a schema into pieces without losing data (lossless join), to test dependency preservation, and to apply the BCNF decomposition and 3NF synthesis algorithms step by step.
Last week you learned to find the normal form of a schema. If the normal form is low, the fix is to split the schema into pieces (decomposition). But not every split is good. You want two things from a decomposition:
- Lossless join: when the pieces are joined back with NATURAL JOIN, you must get exactly the original table.
- Dependency preservation: every FD must be checkable in the individual pieces, without a join.
Typical exam questions: “Is this decomposition lossless?”, “Is it dependency preserving?”, “Decompose R into BCNF”, “Apply the 3NF synthesis algorithm”.
A lossy decomposition: seeing it in data
From the sample database, derive a table that holds each student’s department and the course they took (S7, whose department is NULL, is left out): enr(stu_id, dept_name, course_id), 13 rows. The only FD is .
Now let us split it in the wrong place:
CREATE TABLE r1 AS SELECT DISTINCT stu_id, dept_name FROM enr; -- 5 rows
CREATE TABLE r2 AS SELECT DISTINCT dept_name, course_id FROM enr; -- 10 rows
SELECT COUNT(*) FROM r1 NATURAL JOIN r2;
18
The 13-row table came back as 18 rows. You can see the excess with EXCEPT:
SELECT stu_id, dept_name, course_id FROM r1 NATURAL JOIN r2
EXCEPT SELECT * FROM enr;
| stu_id | dept_name | course_id |
|---|---|---|
| S2 | CS | CS305 |
| S2 | CS | MA101 |
| S5 | CS | CS201 |
| S5 | CS | CS305 |
| S5 | CS | MA101 |
These rows are called spurious tuples. Why only CS? The join is made on dept_name, and CS has three students (S1, S2, S5). r2 keeps the information “a CS student took this course” but has lost which student it was. EE and MATH each have a single student, so no mix-up occurs there.
PITFALL · “lossy” does not mean losing rows. In a lossy decomposition the join produces more rows. What is lost is information: you can no longer tell which rows are real.
The correct split: r1(stu_id, dept_name) and r3(stu_id, course_id). The intersection is stu_id, and since , stu_id is the key of r1. r1 NATURAL JOIN r3 gives exactly 13 rows, and the EXCEPT result is empty.
The binary test
If you split into and , the decomposition is lossless if and only if one of these FDs is in :
That is, the intersection must be a superkey of one of the pieces. Calculation: take the closure of the intersection.
, :
| Decomposition | Intersection | Closure | Result |
|---|---|---|---|
| (A,B), (A,C,D) | A | lossless | |
| (A,B,C), (C,D) | C | lossless | |
| (A,B), (C,D) | empty | empty | lossy |
| (A,B,D), (B,C) | B | lossy |
Three or more pieces: the chase test
The binary test is only for two pieces. For more, use the tableau method:
- The columns are the attributes of R, with one row per piece.
- In each row, write a for the columns the piece contains and b (specific to the row) for the others.
- For each : for two rows that are equal on the X columns, equate their Y values (if one is a, both become a).
- Repeat until there are no changes. If a row consisting entirely of a’s appears, it is lossless.
Worked example. , , pieces (A,B), (C,D), (A,C,E).
A B C D E
R1(AB) a a b b b
R2(CD) b b a a b
R3(ACE) a b a b a
A -> B : R1 and R3 are equal on A → R3.B = a
C -> D : R2 and R3 are equal on C → R3.D = a
R3 : a a a a a → LOSSLESS
If you try (A,B), (C,D), (B,D,E) with the same F, no FD can be applied (there are no two rows equal on A or on C, and no two rows equal on BD), and no all-a row appears: lossy. An intuition (not a rule): in the first example the winning row was R3, which contains the candidate key AC. In the second example there is no piece that holds A and C together.
Dependency preservation
Let be the projection of onto : the FDs in all of whose attributes are inside . A decomposition is preserving if . Practical test: for each in F, take the closure of using only the projected FDs; if Y comes out, it is preserved.
Example 1. , , pieces (A,B), (A,C), (C,D).
The projections: , , . With this set : is lost. The decomposition is lossless but not preserving. If you had chosen the pieces (A,B), (B,C), (C,D), all three FDs would be inside a piece.
Example 2. , , pieces (A,B), (B,C). No piece contains . But since , there is in the projection onto AB and in the projection onto BC. : is preserved.
PITFALL · the projection is not taken from F alone. When finding the FDs that fall in a piece, it is not enough to look at the list in F and ask “are the attributes of this FD in the piece?”. You must also think of the FDs coming from ; the way to do this is to take the closure of every subset in the piece.
The BCNF decomposition algorithm
result := {R}
while there is a Ri in result that is not in BCNF:
find an X -> Y that holds in Ri and where X is not a superkey
replace Ri with (X+ ∩ Ri) and Ri − (X+ − X)
In every split the intersection is X and X is the key of the first piece, so the result is always lossless. Preservation is not guaranteed.
Worked example. , . A and C are not on the right, and = ABCDE: the only key is AC. At each step we choose the shortest violating left side.
ABCDE : A -> B violates. A+ ∩ Ri = AB → AB and ACDE
AB : BCNF
ACDE : D -> E violates. D+ ∩ Ri = DE → DE and ACD
DE : BCNF
ACD : projection AC -> D, AC is a key → BCNF
Result: AB, DE, ACD
Preservation check: . B and C are not in the same piece, and with the projections . Lost. If you had started with in the first step, you might have found a different result; decomposition is not unique, so write down the order you chose in the exam.
University example: wide(S, N, C, T, G) = (stu_id, stu_name, course_id, title, grade), . The algorithm gives CT, NS and CGS: course(course_id, title), student(stu_id, stu_name), takes(stu_id, course_id, grade). This time all three FDs are preserved; this is the answer to why the tables we use in the course are split this way.
The 3NF synthesis algorithm
1. Find the canonical cover Fc of F
2. For every X -> Y in Fc, create a schema (X ∪ Y)
3. If no schema contains a candidate key of R, add a candidate key as a separate schema
4. Delete the schemas that are a subset of another schema
The result is a 3NF decomposition that is both lossless and dependency preserving.
The same example: (nothing is redundant). The schemas are AB, BCD, DE. None contains AC, so add AC. The result is AB, BCD, DE, AC. The chase test says lossless, and each of the three FDs is inside a piece. In this example all the pieces even came out as BCNF; this is not always the case.
BCNF or 3NF?
| BCNF decomposition | 3NF synthesis | |
|---|---|---|
| Lossless | always | always |
| Dependency preservation | not guaranteed | always |
| Redundancy | no FD-caused redundancy | some may remain |
The classic dilemma: tutoring(stu_id, subject, tutor_id), , . BCNF decomposition gives (tutor_id, subject) and (stu_id, tutor_id). It is lossless, but the rule “a student has a single tutor in a subject” can no longer be checked in a single table. The designer often chooses to stay in 3NF and keep this rule enforceable.
Common mistakes
| Wrong idea | The truth |
|---|---|
| A lossy join loses rows | It produces extra (spurious) rows, and information is lost |
| If the intersection is not empty, it is lossless | The intersection must be a superkey of a piece |
| Applying the binary test to 3+ pieces | Use the chase tableau |
| If no piece contains the FD, it is not preserved | The projection is taken from ; it can be derived through a chain |
| BCNF decomposition preserves dependencies | It is lossless but may not preserve |
| Skipping the key step in 3NF synthesis | If no piece contains a key, it can be lossy |
| BCNF decomposition has a single answer | The order in which violations are chosen can give different results |
Test yourself
Exam-style questions. Solve them on paper first, then reveal the answer.
Let with . Is the decomposition , lossless?
Show answer
Binary test: . , so . The intersection is a superkey of one of the pieces: lossless.
Same and . Is the decomposition , lossless?
Show answer
The intersection is the empty set. The empty set determines nothing, the join turns into a Cartesian product and produces rows that are not in the original. Lossy.
Same and . Is , lossless?
Show answer
The intersection is {B}. : B determines neither nor . The binary test fails, so it is lossy.
Table enr(stu_id, dept_name, course_id) is split into r1(stu_id, dept_name) and r2(dept_name, course_id). S5 is a CS student who took only CS101. The CS students in enr took CS101, CS201, CS305 and MA101. After r1 NATURAL JOIN r2, which spurious courses appear for S5?
Show answer
The join is made on dept_name. S5 → CS, CS → {CS101, CS201, CS305, MA101}. So the join produces four courses for S5; S5 took only CS101, and the other three are spurious tuples. The intersection dept_name is the key of neither piece, so it is lossy. In the whole table, 13 real rows grow to 18, with 5 spurious.
Let with . Use the chase (tableau) test to decide whether , , is lossless.
Show answer
Tableau: one row per piece; write a for the columns in the piece and b for the others.
R1: a a b b b, R2: b b a a b, R3: a b a b a.: R1 and R3 are equal on A, so R3's B becomes a. : R2 and R3 are equal on C, so R3's D becomes a. R3 = a a a a a. A row of all a's appeared: lossless.
Same and . Is , , lossless?
Show answer
Tableau: R1: a a b b b, R2: b b a a b, R3: b a b a a.: there are no two rows equal on A. : there are no two rows equal on C. : R1 and R3 are equal on B but different on D. No FD can be applied and no row is all a. Lossy. (Also, no piece contains both A and C; the key AC is in no piece.)
Let with and the decomposition , , . Is it dependency preserving? If not, which FD is lost?
Show answer
Projections: in AB; in AC; in CD. With this set, , with no C. cannot be checked in any piece and cannot be derived: not preserved. (The decomposition is still lossless.)
Let with , decomposed into and . No piece contains both and . Is preserved?
Show answer
The projection includes not only the FDs in F but also those coming from . Projection onto AB: and (because ). Projection onto BC: , . With this set, : , , and A is in it. is preserved. Lesson: "no piece contains the FD" does not mean "not preserved".
Let with . Decompose into BCNF (at each step use the shortest violating left side). Is the result dependency preserving?
Show answer
The key is AC. 1) violates, : R → AB and ACDE.
2) In ACDE, violates (): DE and ACD.
3) ACD: the only FD is , AC is a key, so BCNF.
Result AB, DE, ACD. For : B and C are not in the same piece, and with the projections . Not preserved.
Same and . Apply the 3NF synthesis algorithm.
Show answer
The canonical cover is already . One schema for each FD: AB, BCD, DE. None contains the candidate key AC, so add AC. None is a subset of another, so nothing is deleted. The result is lossless and preserves dependencies (in this example all the pieces are even BCNF).
tutoring(stu_id, subject, tutor_id) has FDs tutor_id -> subject and stu_id, subject -> tutor_id. Give its BCNF decomposition and the FD that is no longer enforceable in a single table.
Show answer
breaks BCNF. = {tutor_id, subject}: one piece is (tutor_id, subject), the other is (stu_id, tutor_id). The intersection tutor_id is the key of the first piece: lossless. But stu_id and subject are no longer in the same table; the rule "a student has a single tutor for a subject" cannot be checked without a join. That is why the schema can be left in 3NF.
Let with . Decompose into BCNF (shortest violating left side first) and name a lost dependency.
Show answer
The keys are AB, BC, BD (3NF but not BCNF). 1) The derived FD violates (, C is alphabetically first among single letters): ACD and BC.
2) In ACD, violates (D is not a superkey): AD and CD.
3) AD, CD, BC have two attributes each, so BCNF.: A and B are not in the same piece, and with the projections . Not preserved. Losslessness is verified with the chase.
Verification: The lossy/lossless join example was counted on the sample database with node:sqlite by running NATURAL JOIN and EXCEPT; the binary test, the chase test, dependency preservation (with FD projection), the BCNF decomposition trace and the 3NF synthesis were computed with verify/SEN2104/lib.mjs (verify/SEN2104/11.mjs, 52 checks, 0 errors). In BCNF decomposition, at each step the shortest (alphabetically first if tied) violating left side was chosen.