When you split one table into two, which property of the functional dependencies guarantees that the split is lossless - that rejoining the fragments cannot invent rows - and what does 'dependency preserving' add on top of that?
answer
- Overlap must be a key of one fragment
- Failure mode is spurious extra rows, never lost rows
- Binary test only; chase for n-ary
- Preservation: every FD checkable inside one table
- Lossless is mandatory, preservation is a trade-off
basics
~20 sA split of R into R1 and R2 is lossless when the shared attributes functionally determine all of R1 or all of R2 - the overlap must be a key of at least one fragment. Dependency preservation additionally requires that every original dependency can be checked inside a single fragment.
solid answer
~60 s**Lossless join.** Decomposing R into R1 and R2 is lossless if and only if the intersection `R1 ∩ R2` determines at least one of the fragments: `(R1 ∩ R2) -> R1` or `(R1 ∩ R2) -> R2` is implied by the dependency set. In plain terms, the shared columns must form a key of one side, so the join matches each row of the other side exactly once. When this fails, rejoining produces **spurious tuples** - combinations that were never in the original data - and the original relation is unrecoverable. **Dependency preservation.** Let Fi be the dependencies of F that mention only attributes of Ri. The decomposition preserves dependencies if the union of the Fi implies all of F. If some dependency spans two fragments, no single-table constraint can enforce it; enforcement requires a join at write time or a trigger, so the rule can be violated by concurrent writes. The two properties are independent. Losslessness is non-negotiable, since without it the design loses information. Dependency preservation is desirable but sometimes impossible, and giving it up means moving a business rule into application or trigger logic.
code
text · 13 linesR(employee, project, department)
ann p1 sales
bob p1 support
R1(employee, project) R2(project, department)
ann p1 p1 sales
bob p1 p1 support
R1 JOIN R2 on project:
ann p1 sales <- original
ann p1 support <- spurious
bob p1 sales <- spurious
bob p1 support <- originalgo deeper
State the rule that the shared columns must be a key of one fragment, and show a small spurious-tuple example.
Apply the test with a closure computation and explain why the join multiplies rows when the overlap is a key of neither side.
Distinguish the two properties, discuss what enforcing a cross-fragment dependency really costs under concurrency, and note the binary-only limitation of the test.
Frame it as a trade-off decision: which rules are worth residual redundancy to keep declarative, and how procedural enforcement decays across write paths over time.
## The problem decomposition solves and creates Splitting a wide relation into narrower ones removes redundancy, but a careless split destroys information. Two criteria judge a split: can the original data be reconstructed, and can the original rules still be enforced. ## Lossless join A decomposition of R into R1 and R2 is **lossless** (better: non-additive) if for every legal instance r of R, ``` projection(r, R1) JOIN projection(r, R2) = r ``` The join can never lose rows - every original row reappears - so the failure mode is always *extra* rows, called spurious tuples. Hence "non-additive join" is the more accurate term. **The test.** The split is lossless if and only if `(R1 ∩ R2) -> R1` or `(R1 ∩ R2) -> R2` is implied by F. Compute the closure of the shared attribute set and see whether it covers either fragment. **Why it works.** If the shared attributes form a key of R2, each row of R1 matches exactly one row of R2 in the join, so no row multiplies. If the overlap is a key of neither side, one shared value can match several rows on both sides, and the cross product manufactures combinations that never existed. **Concrete failure.** Take `R(employee, project, department)` where an employee works on many projects and belongs to one department. Split into `R1(employee, project)` and `R2(project, department)`. The overlap is `{project}`, and `project` is a key of neither fragment - a project has many employees and, across employees, several departments. Rejoining pairs every employee on a project with every department on that project, fabricating rows that assert employees belong to departments they never did. Splitting instead into `R1(employee, project)` and `R2(employee, department)` gives overlap `{employee}`, which is a key of R2 because `employee -> department`, so that split is lossless. **Binary versus n-ary.** The two-way test above is only for binary splits. For decomposition into three or more fragments the general test is the chase algorithm; in practice, splits produced by repeatedly applying the binary rule are lossless by construction, and the synthesis approach guarantees losslessness by ensuring some fragment contains a candidate key of the original relation. ## Dependency preservation Even a lossless split can make a rule unenforceable. Define the **projection** of F onto Ri as all dependencies in F+ whose attributes lie entirely within Ri. The decomposition is **dependency preserving** when the union of these projections implies the whole of F. If a dependency `X -> Y` ends up with X in one fragment and Y in another, no constraint on a single table can enforce it. Enforcement then requires either a join executed on every write - which is racy unless serialised or backed by extra locking - or a trigger, or acceptance that the rule can be broken. In a concurrent system two transactions can each insert a row that is individually legal while jointly violating the cross-table dependency, and neither sees the other. ## The two properties are independent You can have losslessness without preservation, and (for badly chosen splits) preservation without losslessness. The classic example is `R(city, street, zip)` with `{city, street} -> zip` and `zip -> city`. Splitting into `(street, zip)` and `(zip, city)` is lossless, since the overlap `{zip}` determines `city` and hence the second fragment. But `{city, street} -> zip` now spans both fragments and cannot be enforced by any single-table constraint: nothing stops two rows that together assign the same city and street to two different zips. ## Which one can you give up? **Losslessness is mandatory.** A lossy decomposition means the original information is not recoverable, so the design is simply wrong. There is no operational mitigation. **Dependency preservation is negotiable but costly.** Losing it converts a declarative constraint into procedural enforcement, which is exactly the class of rule that decays: it is enforced in one write path and forgotten in a backfill script or an admin tool. When a decomposition forces the choice, options are to accept the cross-fragment check with an explicit trigger or serialising constraint, to keep a stricter but slightly redundant design that preserves the dependency, or to relax the business rule if it turns out nobody actually needs it. Algorithmically, decomposition procedures that eliminate all dependency-based redundancy always achieve losslessness but may fail to preserve dependencies, while synthesis-style procedures built from a minimal cover achieve both at the cost of tolerating some residual redundancy. Recognising that this is a genuine trade-off rather than an oversight is the senior-level point. ## Practical checklist for reviewing a proposed split 1. What attributes do the fragments share? 2. Do those shared attributes form a key of at least one fragment? If not, reject the split. 3. List the original dependencies. Does each one live entirely inside one fragment? 4. For any that do not, decide explicitly how the rule will be enforced and write that decision down.
- Can a lossy decomposition lose rows as well as invent them?No. Projecting and rejoining always returns at least every original row, because each original row contributes its projections to both fragments and they rejoin on the shared attributes. The only possible defect is extra combinations, which is why the property is more precisely called a non-additive join. Those spurious rows are what makes the original instance unrecoverable.
- If a decomposition is lossless but not dependency preserving, what do you actually do about it?You must enforce the cross-fragment dependency outside a single-table constraint - typically a trigger or an application check backed by appropriate locking, since two concurrent inserts can each be locally valid yet jointly violate the rule. The alternative is to choose a different decomposition that keeps the rule inside one table, accepting some residual redundancy. Either way the decision should be explicit and documented rather than silently ignored.
saying these in an interview costs you the question
- Believing a lossy decomposition loses rows rather than adding spurious ones
- Assuming any split that keeps a shared column is safe, without checking the overlap is a key
- Applying the binary lossless test directly to a three-way decomposition
- Treating dependency preservation as guaranteed whenever the split is lossless
- Claiming a cross-table dependency can be enforced reliably by an application check with no locking