Loading learning content...
In the previous page, we defined equivalence between FD sets as mutual coverage—each set covers the other. But what exactly does it mean for one FD set to cover another? This asymmetric relationship is both more fundamental and more frequently used than full equivalence.
The cover relationship answers a practical question that arises constantly in database design: Given that I have a set of FDs F, does another set G capture all the constraints that F imposes? This is subtly different from asking if F and G are equivalent—G might impose additional constraints beyond those in F.
Understanding the cover concept is essential for:
By the end of this page, you will master the formal definition of FD set coverage, understand the asymmetric nature of the cover relationship, learn efficient algorithms to test if one FD set covers another, and apply coverage testing to practical database design problems.
We now establish the precise mathematical definition of what it means for one FD set to cover another.
Definition: FD Set Cover
Let F and G be two sets of functional dependencies over the same attribute set U. We say that F covers G (also written as F ⊇ₗ G, or "F logically contains G") if and only if every functional dependency in G can be derived from F.
Formally:
$$F\ \text{covers}\ G \iff G^+ \subseteq F^+$$
Equivalently:
$$F\ \text{covers}\ G \iff \forall (X \to Y) \in G : (X \to Y) \in F^+$$
In words: F covers G if every FD that is in G's closure is also in F's closure. Since G ⊆ G⁺, this means every FD explicitly in G, plus every FD derivable from G, must be derivable from F.
When F covers G, enforcing F is 'at least as strong' as enforcing G. Any relation that satisfies all FDs in F will automatically satisfy all FDs in G. You can replace G with F without losing any constraints that G imposed.
Relationship to Equivalence
Recall that F ≡ G (equivalence) means F⁺ = G⁺. The cover relationship gives us:
Thus equivalence is mutual coverage, while the cover relationship itself is asymmetric.
Simplified Testing (Key Optimization)
To test if F covers G, we don't need to compute full closures. It suffices to check that, for each FD (X → Y) ∈ G, the right-hand side Y is contained in the attribute closure of X under F:
$$F\ \text{covers}\ G \iff \forall (X \to Y) \in G : Y \subseteq X^+_F$$
This is because (X → Y) ∈ F⁺ if and only if Y ⊆ X⁺_F.
123456789101112131415161718192021222324
function COVERS(F, G): // Returns true if F covers G // i.e., every FD in G is derivable from F for each (X → Y) in G: closure_X = ATTRIBUTE_CLOSURE(X, F) if Y ⊄ closure_X: // If any attribute in Y is not in X⁺_F return false // F does not cover G return true // All FDs in G are derivable from F function ATTRIBUTE_CLOSURE(X, FDSet): // Standard attribute closure algorithm closure = X repeat: old_closure = closure for each (A → B) in FDSet: if A ⊆ closure: closure = closure ∪ B until closure = old_closure return closureA critical aspect of the cover relationship is its asymmetry. Just because F covers G does not mean G covers F. Understanding this asymmetry is essential for correctly reasoning about FD set relationships.
Example Demonstrating Asymmetry
Consider attributes {A, B, C} with:
Does F cover G?
We check if A → B (the only FD in G) is derivable from F:
✓ F covers G.
Does G cover F?
We check each FD in F:
A → B: Compute {A}⁺_G = {A, B} (starting with A, we get B from A → B)
B → C: Compute {B}⁺_G = {B} (starting with B, no FD in G has B on the left)
✗ G does not cover F.
The relationship is asymmetric: F covers G, but G does not cover F.
When F covers G but not vice versa, F is 'stronger' than G—it imposes all of G's constraints plus additional ones. If you replace F with G, you lose constraints (B → C in this example). If you replace G with F, you only gain constraints, never lose them.
Visualizing Asymmetry
We can visualize the relationship between F and G's closures:
The Venn-like diagram shows that G⁺ is a proper subset of F⁺. F covers G (everything in G⁺ is in F⁺), but G doesn't cover F (things in F⁺ are not in G⁺).
The cover relationship is intimately connected to logical implication of functional dependencies. Understanding this connection deepens our theoretical grasp and clarifies practical applications.
Logical Implication of a Single FD
We say that F logically implies a functional dependency X → Y (written F ⊨ X → Y) if every relation instance that satisfies all FDs in F must also satisfy X → Y.
By the soundness and completeness of Armstrong's axioms:
$$F \vDash (X \to Y) \iff (X \to Y) \in F^+$$
Cover as Universal Implication
F covers G if and only if F logically implies every FD in G:
$$F\ \text{covers}\ G \iff \forall (X \to Y) \in G : F \vDash (X \to Y)$$
Since Armstrong's axioms are sound and complete, this is equivalent to:
$$F\ \text{covers}\ G \iff \forall (X \to Y) \in G : (X \to Y) \in F^+$$
When F covers G, any database state that satisfies F will automatically satisfy G. The constraints in F are sufficient to enforce all constraints in G. This is why coverage is central to dependency preservation in decomposition—we want the decomposed FDs to cover the original FDs.
The Role of Trivial FDs
Recall that a trivial FD is one of the form X → Y where Y ⊆ X. Trivial FDs are in every closure and are always satisfied. They play a subtle role in coverage:
Minimal Cover Intuition
If F covers G and F has fewer or simpler FDs than G, then F is a more efficient representation. This leads to the concept of minimal cover or canonical cover—the smallest F that covers (and is covered by) the original FD set.
We can characterize a minimal cover as an FD set that:
Let's work through several examples of increasing complexity to build solid intuition for the cover relationship.
Example 1: Simple Cover Check
Let U = {A, B, C, D} with:
Question: Does F cover G?
Solution: Check if A → D is derivable from F:
✓ F covers G.
Example 2: Cover Check with Composite LHS
Let U = {A, B, C, D} with:
Question: Does F cover G?
Solution: Check if AC → BD is derivable from F:
✓ F covers G.
Note: Even though F has FDs with single-attribute LHS, it can derive an FD with composite LHS.
Example 3: Non-Cover Scenario
Let U = {A, B, C, D} with:
Question: Does F cover G?
Solution: Check each FD in G:
A → B: Compute {A}⁺_F:
B → C: Compute {B}⁺_F:
✗ F does NOT cover G.
F cannot derive B → C because no FD in F has B on the left-hand side.
| Scenario | F | G | Does F Cover G? | Key Insight |
|---|---|---|---|---|
| Transitive chain | {A→B, B→C, C→D} | {A→D} | Yes | Transitivity can derive long-range FDs |
| Composite from simple | {A→B, C→D} | {AC→BD} | Yes | Augmentation combines independent FDs |
| Missing link | {A→B, C→D} | {A→B, B→C} | No | Can't derive new FD without LHS match |
Example 4: Mutual Coverage (Equivalence)
Let U = {A, B, C} with:
Question: Do F and G mutually cover each other?
Does F cover G?
✓ F covers G.
Does G cover F?
✓ G covers F.
Conclusion: F ≡ G (they are equivalent).
This confirms the decomposition rule: an FD with composite RHS is equivalent to multiple FDs with singleton RHS.
One of the most important applications of the cover concept is in dependency-preserving decomposition. When we decompose a relation into smaller relations, we need to ensure that the FDs that can be enforced on the decomposed relations collectively cover the original FDs.
The Decomposition Problem
Consider a relation R with attribute set U and FD set F. When we decompose R into R₁, R₂, ..., Rₙ with attribute sets U₁, U₂, ..., Uₙ, each Rᵢ can only enforce FDs that involve attributes entirely within Uᵢ.
Let Fᵢ = π_{Uᵢ}(F) be the projection of F onto Uᵢ—the set of FDs in F⁺ that involve only attributes in Uᵢ.
The decomposition is dependency-preserving if:
$$(F_1 \cup F_2 \cup ... \cup F_n)^+ \supseteq F^+$$
That is, the union of projected FD sets covers the original FD set.
If F is not covered by the union of projected FDs, then some constraints from the original design cannot be enforced locally on the decomposed relations. Enforcing them would require expensive cross-table checks or application-level logic, undermining the benefits of proper database design.
Example: Dependency-Preserving Decomposition
Consider R(A, B, C) with F = {A → B, B → C}.
Decompose into:
Check: Does F₁ ∪ F₂ = {A → B, B → C} cover F = {A → B, B → C}?
Each FD in F is in F₁ ∪ F₂, so trivially yes.
✓ This decomposition is dependency-preserving.
Example: Non-Preserving Decomposition
Consider R(A, B, C) with F = {A → B, B → C, A → C}.
Suppose we decompose into:
Check: Does F₁ ∪ F₂ = {A → B, A → C} cover F?
The FD B → C cannot be derived from F₁ ∪ F₂.
✗ This decomposition is not dependency-preserving.
The constraint "every B value determines a C value" is lost. It cannot be enforced by looking at R₁ and R₂ separately.
When normalizing a schema, always verify that your decomposition preserves dependencies. Use the cover test: compute the attribute closure of each FD's LHS under the union of projected FD sets and check if the RHS is included.
While the theoretical definition of coverage involves closures of potentially exponential size, practical cover testing is highly efficient due to the attribute closure technique.
Time Complexity Analysis
To test if F covers G:
Let:
Each attribute closure computation takes O(n × k) time (we iterate over FDs in F up to k times until fixed point).
For m FDs in G, total time is O(m × n × k).
This is polynomial time, making cover testing practical even for large schemas.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546
function COVERS(F, G): """ Tests if F covers G in O(|G| × |F| × |attributes|) time. Returns true if every FD in G is derivable from F. """ for each (X → Y) in G: # Compute attribute closure of X under F closure = ATTRIBUTE_CLOSURE_OPTIMIZED(X, F) # Check if all attributes in Y are in the closure for each attribute a in Y: if a not in closure: return false # Found an FD in G not derivable from F return true function ATTRIBUTE_CLOSURE_OPTIMIZED(X, F): """ Optimized attribute closure using a work-list approach. Avoids redundant scans of FD set. """ closure = set(X) # Track which FDs remain to be checked remaining_fds = list(F) changed = true while changed: changed = false new_remaining = [] for each (A → B) in remaining_fds: if A ⊆ closure: # Add B to closure old_size = len(closure) closure = closure ∪ B if len(closure) > old_size: changed = true # Don't add this FD back to remaining else: new_remaining.append(A → B) remaining_fds = new_remaining return closureOptimization: Early Termination
When checking if F covers G, we can short-circuit as soon as we find a single FD in G that isn't derivable from F:
Optimization: Caching Closures
If we need to test coverage repeatedly (e.g., during an iterative canonical cover algorithm), we can cache computed attribute closures:
For one-time cover tests, caching adds overhead, but for iterative algorithms, it can provide significant speedup.
The cover relation has several important mathematical properties that are essential for reasoning about FD sets.
Reflexivity
Every FD set covers itself: F covers F.
This is trivially true since F⁺ ⊆ F⁺.
Transitivity
If F covers G and G covers H, then F covers H.
Proof: G⁺ ⊆ F⁺ and H⁺ ⊆ G⁺ implies H⁺ ⊆ F⁺.
Anti-symmetry (Up to Equivalence)
If F covers G and G covers F, then F ≡ G.
This follows directly from the definition: G⁺ ⊆ F⁺ and F⁺ ⊆ G⁺ implies F⁺ = G⁺.
These properties make the cover relation a preorder (a reflexive, transitive relation) on FD sets, with equivalence classes defined by mutual coverage.
While the cover relation on individual FD sets is only a preorder (not antisymmetric), it induces a partial order on equivalence classes. For equivalence classes [F] and [G], we have [F] ≤ [G] if F covers G. This partial order has a maximum element (the class of all FD sets that determine everything from the empty set) and a minimum element (the class of trivial FDs only).
Additional Properties
The cover concept is a fundamental building block in FD theory, underlying equivalence, canonical covers, and dependency preservation. Let's consolidate the key insights.
What's Next:
With the cover concept firmly established, we're now equipped to tackle the next topic: Deriving FDs. We'll explore systematic methods for deducing new functional dependencies from a given set, using Armstrong's axioms and their derived rules. This combines with our coverage understanding to form the complete theoretical toolkit for FD analysis.
You now have a deep understanding of the cover relationship between FD sets. This asymmetric foundation is essential for computing canonical covers, verifying decompositions, and understanding equivalence. Next, we'll systematically explore how to derive new FDs from existing ones.