Attribute closure and candidate keys
Compute X+ by starting with X and repeatedly applying dependencies whose left sides are included, until nothing changes. X is a superkey when its closure contains every attribute; a candidate key additionally has no proper subset that is a superkey.
Check yourself: Can removing one attribute from your proposed key still determine the relation?
Normalization and decomposition
For every nontrivial dependency X → A, BCNF requires X to be a superkey. Third normal form also permits A to be prime, meaning it belongs to some candidate key. Check lossless join and dependency preservation separately; neither label substitutes for the other.
Check yourself: In a binary decomposition, does the shared attribute set determine either component?
Relational operations and SQL semantics
Relational algebra uses sets; SQL normally retains duplicates. Joins can multiply rows through multiple matches. WHERE retains only TRUE, so NULL comparisons need care. COUNT(column) ignores NULL values; COUNT(*) counts rows. DISTINCT changes multiplicity, not the join condition.
Check yourself: Can you enumerate the joined rows before grouping or counting them?
Conflict serializability
Create one precedence-graph vertex per transaction. Add an edge for ordered operations from different transactions on the same item when either writes. Acyclicity establishes conflict serializability; a topological order supplies an equivalent serial order. It does not establish recoverability.
Check yourself: Have you excluded read-read pairs and checked every shared data item?
Indexes and access cost
B+ trees support ordered traversal and ranges; hashing primarily supports equality lookup. An index lookup can still require data-page reads, especially for scattered matches. Calculate page accesses using index height, caching assumptions, and record layout rather than assuming an index eliminates I/O.
Check yourself: Could data-page access dominate the cost of this range query?