Functional Dependencies and Database Design

1

Functional Dependencies and Database Design

Functional dependencies are among the most powerful conceptual tools available to a database designer. Rather than treating a schema as a collection of tables invented ad hoc, a designer who works with functional dependencies treats the schema as a logical consequence of the real-world rules that govern the data. Every attribute relationship that matters in the business domain — every constraint that says "this value determines that value" — can be expressed as a functional dependency, and once expressed, it acts as a precise blueprint for how relations should be structured. Understanding this connection between dependencies and design decisions is what separates principled database engineering from guesswork.

A functional dependency is a constraint between two sets of attributes in a relation. We write X → Y and read it as "X functionally determines Y," meaning that for any two tuples in the relation, if they agree on all attributes in X, they must also agree on all attributes in Y. This is not a statistical observation about the current data — it is a semantic rule that must hold for every possible state of the database, now and forever. That distinction is critical: you cannot discover a functional dependency simply by looking at the data in front of you; you must understand the business rules that produced that data.

Functional Dependencies as Design Blueprints

When a designer sits down to model a domain before a single table has been defined, cataloging the functional dependencies that apply in that domain is one of the most productive first steps possible. These dependencies reveal which attributes logically belong together, which attributes are independent of one another, and where the true keys of the data reside.

Consider a simple order-management domain. Before creating any tables, a designer might observe the following real-world rules:

  • Each order has exactly one customer.
  • Each customer has exactly one billing address.
  • Each product has exactly one price and one product name.
  • Each order line (identified by an order and a product together) has exactly one quantity.

Translating these rules into functional dependencies produces:

OrderID         → CustomerID
CustomerID      → BillingAddress, CustomerName
ProductID       → ProductName, UnitPrice
OrderID, ProductID → Quantity

These four dependencies immediately suggest a schema. OrderID → CustomerID tells us that orders and customers are related, but that their attributes belong in separate structures. CustomerID → BillingAddress, CustomerName tells us that customer information forms a natural grouping with CustomerID as its key. ProductID → ProductName, UnitPrice defines a product entity. OrderID, ProductID → Quantity defines a line-item entity whose key is composite.

Without this exercise, a designer might combine all these attributes into one large flat table, or make arbitrary grouping decisions based on what "feels right." The FDs eliminate that arbitrariness. They expose the implicit business rules embedded in the data and translate those rules into structural guidance. A business rule such as "each order belongs to exactly one customer" is not just a description of reality — it is a constraint on the data model, and functional dependency notation gives that constraint a precise, actionable form.

Using FDs as blueprints also reduces the risk of creating tables that reflect programmer convenience rather than domain logic. A table that mixes order-level information with customer-level information is convenient to query in some cases, but it violates the dependency structure of the domain and will cause serious problems as the database evolves.

Inferring Schema Structure from Dependencies

Once the functional dependencies are on paper, the process of inferring a schema structure from them follows a systematic logic. Each dependency of the form X → Y suggests a candidate relation in which X is the key and Y consists of dependent attributes. When multiple dependencies share the same left-hand side, their right-hand sides can be combined into a single relation governed by that shared determinant.

Suppose we have the following set of functional dependencies for a university enrollment system:

StudentID       → StudentName, Major, AdvisorID
AdvisorID       → AdvisorName, Department
CourseID        → CourseName, Credits, DepartmentID
DepartmentID    → DepartmentName
StudentID, CourseID → Grade, Semester

Grouping by determinant, we immediately see four natural relations:

  • Student(StudentID, StudentName, Major, AdvisorID)
  • Advisor(AdvisorID, AdvisorName, Department)
  • Course(CourseID, CourseName, Credits, DepartmentID)
  • Department(DepartmentID, DepartmentName)
  • Enrollment(StudentID, CourseID, Grade, Semester)

Notice that AdvisorID appears in the Student relation as a foreign key, reflecting the dependency chain StudentID → AdvisorID → AdvisorName, Department. If we had instead placed AdvisorName and Department directly in the Student relation, we would be mixing two different dependency levels — attributes determined by StudentID sitting alongside attributes determined by AdvisorID — and that mixture is precisely the source of anomalies, as we will see shortly.

Overlapping dependencies are also revealing. If two different functional dependencies share some right-hand-side attributes, the designer should ask whether those attributes truly belong to both determinants or whether one of the dependencies is a derived consequence of the other. Similarly, when a single attribute appears on the right-hand side of dependencies with different left-hand sides, that often signals that the attribute is playing multiple roles and the schema needs to separate those roles into distinct relations.

Grouping attributes by their determinants is therefore not just a mechanical exercise — it is a process of aligning the schema with the real-world entity boundaries that the data is meant to represent. A relation whose attributes are all determined by its key, and nothing else, corresponds to a coherent real-world concept. A relation that mixes attributes from different determinants is an artificial construction that the data model will eventually be punished for.

Redundancy and Anomalies Caused by Ignored Dependencies

When functional dependencies are ignored during schema design, the resulting relations almost always contain redundancy, and redundancy leads directly to anomalies. There are three classical anomaly types, and each one can be traced to a specific kind of dependency violation.

Imagine that instead of the separate Student and Advisor relations above, we store everything in one flat relation:

StudentAdvisor(StudentID, StudentName, Major, AdvisorID, AdvisorName, Department)

Because AdvisorID → AdvisorName, Department, the same advisor's name and department will appear in every row for every student that advisor supervises. If Dr. Smith supervises 200 students and changes departments, we must update 200 rows — and if even one update is missed, the database now contains a contradiction. This is an update anomaly: a single real-world fact governed by a functional dependency must be changed in multiple places simultaneously, and inconsistency is the price of failure.

An insertion anomaly arises when we try to add new information that cannot be represented without the presence of unrelated data. Suppose we want to record a new advisor who has just been hired but has not yet been assigned any students. In the flat StudentAdvisor relation, there is no row in which to store that advisor's information, because the primary key requires a StudentID. The advisor's existence cannot be recorded until at least one student is assigned — a logically absurd constraint that the schema imposes artificially.

A deletion anomaly is the mirror image of the insertion anomaly. Suppose an advisor's last student graduates and their enrollment record is deleted. If AdvisorName and Department exist only in rows associated with that advisor's students, deleting those rows destroys the only copy of the advisor's information. A record of a real-world entity — the advisor — vanishes as a side effect of deleting a different fact entirely.

All three anomalies share the same root cause: the relation contains attributes that are determined by something other than the relation's primary key. The functional dependency AdvisorID → AdvisorName, Department holds within the relation, but AdvisorID is not the key — StudentID is. This mismatch between the actual determinant and the declared key is the structural flaw that normalization is designed to correct.

Functional Dependencies as the Foundation for Normalization

Normalization is the process of restructuring a schema to eliminate redundancy and anomalies by ensuring that relations satisfy progressively stricter constraints. Each normal form defines which types of functional dependencies are permissible within a single relation, and violations of those constraints are resolved by decomposing the offending relation into two or more relations that together hold the same information without the problematic dependency structure.

First Normal Form (1NF) requires that all attributes be atomic — no repeating groups, no multi-valued attributes. While 1NF does not explicitly reference functional dependencies in the same way later forms do, it establishes the relational foundation on which dependency analysis can operate.

Second Normal Form (2NF) eliminates partial dependencies: a non-key attribute must not be functionally dependent on a proper subset of a composite primary key. For example, if a relation has a composite key (StudentID, CourseID) and also contains StudentName, which depends only on StudentID, then StudentName is partially dependent on the key. 2NF requires that StudentName be moved to a separate relation keyed by StudentID alone.

Third Normal Form (3NF) eliminates transitive dependencies: a non-key attribute must not be functionally dependent on another non-key attribute. In the StudentAdvisor example, AdvisorName depends on AdvisorID, which in turn depends on StudentID — a transitive chain. 3NF requires that transitive dependencies be broken by extracting the intermediate determinant and its dependents into their own relation.

Boyce-Codd Normal Form (BCNF) is a stricter version of 3NF: for every functional dependency X → Y that holds in a relation, X must be a superkey of that relation. BCNF eliminates certain anomalies that 3NF allows in edge cases involving overlapping candidate keys.

The key insight is that you cannot diagnose or fix these problems without first cataloging the functional dependencies. Normalization is not a procedure for making tables "cleaner" in some vague aesthetic sense — it is a precise mathematical process of identifying FD violations and resolving them through decomposition. A designer who has not identified the FDs present in a relation cannot determine which normal form the relation satisfies, cannot identify which attributes need to be extracted, and cannot verify that a proposed decomposition achieves the intended result. Functional dependencies are not merely relevant to normalization — they are the language in which normalization is conducted.

Lossless Decomposition and Dependency Preservation

When a relation is decomposed to eliminate a dependency violation, two critical properties must be evaluated: whether the decomposition is lossless and whether it preserves dependencies.

A decomposition of relation R into relations R1 and R2 is lossless (also called lossless-join) if the natural join of R1 and R2 produces exactly R — no more tuples and no fewer. The condition for a lossless decomposition when splitting R into R1 and R2 is that the attributes common to both resulting relations (their intersection) must functionally determine at least one of the two relations. Formally, if R1 ∩ R2 → R1 or R1 ∩ R2 → R2 holds, the decomposition is lossless.

For example, suppose we decompose:

StudentAdvisor(StudentID, StudentName, AdvisorID, AdvisorName, Department)

into:

Student(StudentID, StudentName, AdvisorID)
Advisor(AdvisorID, AdvisorName, Department)

The intersection of the two result sets is {AdvisorID}. Since AdvisorID → AdvisorName, Department, the intersection determines the Advisor relation. Therefore the decomposition is lossless — joining Student and Advisor on AdvisorID will reconstruct the original relation exactly, with no spurious tuples introduced.

A lossy decomposition, by contrast, introduces extra tuples when the pieces are joined — tuples that were not in the original relation. This means information has been corrupted: the decomposed schema cannot faithfully represent the original data, making the design worse than useless for query purposes.

Dependency preservation is a separate but equally important property. A decomposition preserves dependencies if every functional dependency in the original relation can be enforced within at least one of the decomposed relations, without needing to join relations back together. If enforcing a dependency requires a join, then the database system cannot check that dependency using a simple constraint on a single table — it would require a complex trigger or a join-based check that the DBMS may not efficiently support.

The tension between BCNF and dependency preservation is a well-known design challenge. It is sometimes impossible to achieve BCNF while preserving all dependencies. In such cases, the designer must choose: accept 3NF (which guarantees dependency preservation) instead of BCNF, or accept BCNF while acknowledging that some dependencies must be enforced through application logic or triggers rather than table-level constraints. This is not a failure of the theory — it is a meaningful trade-off that the theory makes explicit and quantifiable, allowing the designer to make an informed decision rather than stumbling into one accidentally.

Consider a classic example involving a relation Teaches(Student, Course, Teacher) where the following FDs hold:

Teacher → Course        (each teacher teaches exactly one course)
Student, Course → Teacher

The candidate keys are {Student, Course} and {Student, Teacher}. The dependency Teacher → Course violates BCNF because Teacher is not a superkey. Decomposing to achieve BCNF gives us:

TeacherCourse(Teacher, Course)
StudentTeacher(Student, Teacher)

But now the dependency Student, Course → Teacher cannot be enforced within either of these two relations alone — it requires joining them. The decomposition achieves BCNF but sacrifices dependency preservation. Depending on the application's requirements, a designer might choose to remain at 3NF to keep all dependencies locally enforceable.

Using Closure and Keys to Validate Schema Decisions

Once a set of functional dependencies has been identified and a proposed schema has been sketched out, the designer needs a rigorous method for validating that schema decisions are correct. The primary tool for this validation is attribute closure.

The closure of a set of attributes X under a set of functional dependencies F, written X⁺, is the set of all attributes that are functionally determined by X given F. To compute it, we start with X⁺ = X and repeatedly apply each dependency in F: if the left-hand side of a dependency is a subset of X⁺, we add the right-hand side attributes to X⁺. We repeat until no more attributes can be added.

For example, given the dependencies:

A → B
B → C
C → D

The closure of {A} is computed as follows:

  • Start: {A}
  • Apply A → B: {A, B}
  • Apply B → C: {A, B, C}
  • Apply C → D: {A, B, C, D}

So {A}⁺ = {A, B, C, D}. If the relation contains only attributes A, B, C, and D, then A is a candidate key — it determines all other attributes.

Computing closure is the standard method for confirming whether a proposed key is truly a key. If the closure of the proposed key equals the set of all attributes in the relation, the key is valid. If not, the proposed key does not functionally determine all attributes and must be augmented or reconsidered.

Closure is also used to identify all candidate keys, which is essential because a relation may have multiple minimal sets of attributes that each determine the whole relation. Missing a candidate key has practical consequences: it may mean the designer imposes an artificial primary key when a natural one exists, or fails to enforce a uniqueness constraint that the business rules require.

The algorithm for finding all candidate keys involves systematically testing subsets of attributes for superkey status (using closure) and then checking minimality (ensuring no proper subset is also a superkey). While this can be computationally intensive for large attribute sets, it guarantees that no valid key is overlooked.

Closure also validates whether a specific functional dependency follows from the known set of FDs — a check that is necessary when determining whether a decomposition preserves a particular dependency. To test whether X → Y is implied by F, compute X⁺ under F and check whether Y is a subset of X⁺. If it is, the dependency is logically entailed by F; if not, F does not imply that dependency.

For instance, suppose a designer is unsure whether the dependency A → D is implied by the FDs above. Computing {A}⁺ gives {A, B, C, D}, which contains D, so yes — A → D is implied, even though it was not listed explicitly. This kind of reasoning prevents designers from either over-specifying dependencies (listing redundant ones) or under-specifying them (missing implied constraints).

Together, these tools — dependency cataloging, anomaly analysis, normalization, decomposition evaluation, and closure computation — form a complete methodology for principled database design. A designer equipped with this methodology does not need to rely on intuition or convention. Every structural decision can be justified by an explicit dependency, every anomaly can be traced to a specific violation, every decomposition can be tested for losslessness and dependency preservation, and every key claim can be verified mathematically. Functional dependencies are not just a theoretical formalism — they are the working language of rigorous relational database design.

NotesConnects FD theory to practical schema design. Covers the full arc from identifying FDs in the domain through anomaly analysis, normalization motivation, decomposition properties, and closure-based validation. Examples progress from order management through university enrollment to illustrate each concept concretely.