CMD Guide
HomeDatabasesFunctional Dependency

Closure of Attribute Sets

The closure X+ is computed by a fixpoint loop: you seed a set with X, then repeatedly scan every functional dependency and, whenever a rule's left side is already fully inside your set, you swallow its right side too — until one full pass adds nothing new. That termination point is the set of every attribute X can functionally determine, and it is the single primitive that powers candidate-key detection, normalization, and dependency checking.

The fixpoint algorithm

Closure is not a single rule application — it is a loop that runs to convergence, because adding one attribute can unlock a dependency that adds the next. Each rule can fire at most once and there are finitely many attributes, so the loop always halts.

closure(X, FDs):
    result = set(X)            # seed with the attributes we start with
    changed = True
    while changed:
        changed = False
        for (lhs -> rhs) in FDs:
            if lhs is a subset of result and not (rhs is a subset of result):
                result = result ∪ rhs
                changed = True   # a new attribute appeared; must re-scan
    return result

The changed flag is the whole trick. A dependency that did nothing on pass 1 (its left side was not yet present) may fire on pass 2 once an earlier rule has grown the set. The loop ends only when a complete pass over all FDs adds zero attributes.

Worked example: a key closure that reaches everything

Take the Student_Course relation with attributes {Student_ID, Course_ID, Instructor, Department} and this set of functional dependencies:

#Functional dependency
1Student_ID → Course_ID
2Course_ID → Instructor
3Instructor → Department

We compute {Student_ID}+. Watch the set grow one rule at a time; each new attribute unlocks the next dependency in the chain:

IterationRule that firesWhy it firesClosure set after
0 (seed)start with X{Student_ID}
1Student_ID → Course_IDStudent_ID ∈ set{Student_ID, Course_ID}
2Course_ID → InstructorCourse_ID just got added{Student_ID, Course_ID, Instructor}
3Instructor → DepartmentInstructor just got added{Student_ID, Course_ID, Instructor, Department}
4 (final pass)noneevery rule's RHS already present{Student_ID, Course_ID, Instructor, Department}

The set reached all four attributes, so {Student_ID}+ = R (the whole relation). That is the definition of a superkey. Because no proper subset of {Student_ID} exists (it is a single attribute), it is also minimal, so Student_ID is a candidate key.

diagram
diagram

The flip side: a closure that stops early proves a non-key

Closure does not only confirm keys — it is how you disprove one. Compute {Course_ID}+ over the same FDs:

IterationRule that firesClosure set after
0 (seed){Course_ID}
1Course_ID → Instructor{Course_ID, Instructor}
2Instructor → Department{Course_ID, Instructor, Department}
3 (final pass)none — Student_ID → Course_ID cannot fire (its LHS Student_ID is not in the set), and no other rule produces Student_ID{Course_ID, Instructor, Department}

{Course_ID}+ = {Course_ID, Instructor, Department}, which is missing Student_ID. Since the closure does not reach all of R, Course_ID is not a superkey — and the missing attribute tells you exactly why: nothing on the right-hand side of any FD ever yields Student_ID, so no attribute set that excludes Student_ID can ever be a key. This is the standard shortcut: an attribute that never appears on the right side of any FD must be part of every candidate key. Here that forces Student_ID into every key, which is consistent with {Student_ID} being the candidate key.

diagram
diagram

Why the naive version is wrong

A common bug is computing closure with a single pass over the FD list instead of looping to a fixpoint. With the rules ordered as listed (1: Student_ID→Course_ID, 2: Course_ID→Instructor, 3: Instructor→Department) a single pass happens to work — but reorder them to {Instructor→Department, Course_ID→Instructor, Student_ID→Course_ID} and seed with {Student_ID}: the first two rules see nothing in the set, fire nothing, and a single pass returns just {Student_ID, Course_ID}, wrongly concluding Student_ID is not a key. The correct algorithm re-scans after every change, so attribute order in the FD list never affects the answer. Always loop until a full pass adds nothing.

Pitfalls

Takeaways


Sources: Silberschatz, Korth & Sudarshan, Database System Concepts (7th ed.), §7.4 on closure of attribute sets and the attribute-closure algorithm; Elmasri & Navathe, Fundamentals of Database Systems, on the membership/closure algorithm and candidate-key inference. The fixpoint algorithm and the right-hand-side rule for forced key attributes are standard relational-theory results. Re-authored and deepened for this guide: added the fixpoint pseudocode, the iteration-by-iteration trace tables, two closure-chain diagrams, a second (non-key) closure that terminates early, the "why the naive single-pass version is wrong" note, and the engineer-facing pitfalls.

🤖 Don't fully get this? Learn it with Claude

Stuck on Closure of Attribute Sets? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Closure of Attribute Sets** (Databases) and want to truly understand it. Explain Closure of Attribute Sets from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Closure of Attribute Sets** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Closure of Attribute Sets** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Closure of Attribute Sets** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes