Database Design: Reading the Relational Model, SQL and Normalization as the Placement of Facts
Prerequisite:Docker and Kubernetes: Why Containers Are Light and Clusters Heal Themselves
0. Key points
Section titled “0. Key points”- A relational database is not a collection of “tables” but a collection of relations (finite sets of tuples). From this point of view every SQL command becomes an operation on sets.
- A primary key is a minimal set of attributes that determines a row; a foreign key is a reference to the primary key of another table. These two notions alone express almost all of the connections between tables.
- Normalization is not a matter of taste. Its justification is a theorem (Proposition 5.2): a state in which the same fact is written in two places necessarily invites update anomalies.
- The first three normal forms strengthen one condition in stages, according to whether the left-hand side of a functional dependency is “part of a key” or “not a key at all”. Deciding them reduces to computing the attribute closure (Lemma 4.3).
- When we split a table, we need to be able to split it and join it back without loss (a lossless-join decomposition). A functional dependency guarantees this automatically (Theorem 6.3).
- NoSQL is not “a database without normalization”. It is a design that moves the join from query time to design time and in exchange makes horizontal partitioning easy. Which one to choose is decided by the access pattern.
1. Motivation: why a spreadsheet is not enough
Section titled “1. Motivation: why a spreadsheet is not enough”When people set out to manage orders, the first thing most of them build is one big table. Each row records who bought what, how many, and when. This does work — for a while. After some use, however, the following always happens.
Example 1.1(How a single table falls apart)
Suppose we build the following table (order 1001 contains two products, so it occupies two rows).
| Order no. | Order date | Customer ID | Customer name | Customer address | Product ID | Product name | Unit price | Quantity |
|---|---|---|---|---|---|---|---|---|
| 1001 | 2026-04-01 | C01 | Tanaka Trading | Chiyoda-ku, Tokyo | P01 | Bolt | 120 | 10 |
| 1001 | 2026-04-01 | C01 | Tanaka Trading | Chiyoda-ku, Tokyo | P02 | Nut | 80 | 25 |
| 1002 | 2026-04-03 | C02 | Sato Industries | Kita-ku, Osaka | P01 | Bolt | 120 | 4 |
Three accidents now occur.
Update anomaly. Tanaka Trading moves. There are two rows whose address must be rewritten. If only one of them is corrected, the same customer ends up with two different addresses. The number of rows grows with the number of orders, so this danger grows with time.
Insertion anomaly. We want to register a new product P03, but nothing has been sold yet. This table can hold only “order rows”, so there is nowhere to record a product on its own. The only option is a ghost row with an empty order number.
Deletion anomaly. If order 1002 is cancelled and its row deleted, the name and address of customer C02 (Sato Industries) vanish from the world as well. What we wanted to delete was an order, not a customer.
All three accidents have the same cause: facts of different natures were made to share one row. The fact “the address of customer C01 is Chiyoda-ku, Tokyo” and the fact “10 units of product P01 were bought in order 1001” arise independently, change independently, and disappear independently. Bundle them into one row and every time we touch one of them the other is caught in the crossfire.
When E. F. Codd proposed the relational model in his 1970 paper, this was exactly his motivation. In the hierarchical and network databases of the time, applications had to know how the data was physically arranged (which record was linked to which pointer), and changing the storage layout broke the programs. Codd argued that if data is described as a mathematical relation and queries are written in a declarative language — relational algebra — then one becomes independent of the physical storage. In the second half of the same paper he introduced a criterion for “which shapes of relation are free of anomalies”, namely the normal forms. The relational model and normalization were a single package from the very beginning.
In this article we define the vocabulary of the relational model (§2), check the basic SQL operations against real data (§3), introduce functional dependencies (§4), and prove why the first three normal forms are needed (§5). We then treat the theorem that guarantees the correctness of a decomposition (§6) and the trade-off with NoSQL (§7).
2. Preliminaries on the relational model
Section titled “2. Preliminaries on the relational model”Definition 2.1(Relation schemas and relations)
A finite set of attributes is called a relation schema. Each attribute is assumed to come with a set of values, its domain .
A map assigning to each a value is called a tuple (a row) over . Writing for the set of all tuples over , a finite subset is called a relation over (the contents of a table, an instance).
For a subset , we write for the restriction of to .
Two points in this definition matter. First, a relation is a set, so the same tuple cannot occur twice. Second, sets have no order, so neither the order of the rows nor the order of the columns carries meaning. Actual SQL products do allow duplicate rows (the result of a SELECT is a multiset) and do impose an order on columns, but when thinking about design it is less confusing to think in terms of sets.
Definition 2.2(Superkeys, candidate keys, primary keys and foreign keys)
Let a relation schema be given, together with the family of relations permitted over (all instances satisfying the constraints). We say that is a superkey if, for every permitted instance ,
holds. A superkey none of whose proper subsets is a superkey is called a candidate key. When there are several candidate keys, the designer picks one and declares it the primary key. An attribute belonging to some candidate key is called a prime attribute; otherwise it is non-prime.
A set of attributes of a relation schema is a foreign key referencing the primary key of a relation schema if we impose the constraint that, for every instance of and the corresponding instance of ,
holds (excluding the case where is NULL). This constraint is called referential integrity.
A primary key is “the address that points at a row”; a foreign key is “a reference to that address”. In the vocabulary of programming languages, the primary key corresponds to object identity, the foreign key to a pointer, and referential integrity to the promise that no dangling pointer is ever created.
Throughout this article we use the following order database as our example, denoting each attribute by a single letter.
| Symbol | |||||||||
|---|---|---|---|---|---|---|---|---|---|
| Meaning | order no. | order date | customer ID | customer name | customer address | product ID | product name | unit price | quantity |
The single table of Example 1.1 is a relation over .
3. Basic SQL operations
Section titled “3. Basic SQL operations”SQL is the de facto standard language for the relational model. Here we check five operations that we will need later, working on the four normalized tables below (we derive them in §5).
CREATE TABLE customers ( customer_id CHAR(3) PRIMARY KEY, name VARCHAR(100) NOT NULL, address VARCHAR(200) NOT NULL);
CREATE TABLE products ( product_id CHAR(3) PRIMARY KEY, name VARCHAR(100) NOT NULL, unit_price INTEGER NOT NULL CHECK (unit_price >= 0));
CREATE TABLE orders ( order_id INTEGER PRIMARY KEY, order_date DATE NOT NULL, customer_id CHAR(3) NOT NULL REFERENCES customers(customer_id));
CREATE TABLE order_items ( order_id INTEGER NOT NULL REFERENCES orders(order_id), product_id CHAR(3) NOT NULL REFERENCES products(product_id), quantity INTEGER NOT NULL CHECK (quantity > 0), PRIMARY KEY (order_id, product_id));REFERENCES is the foreign key declaration: it tells the DBMS to enforce the referential integrity of Definition 2.2. An attempt to insert an order with a nonexistent customer_id is rejected by the DBMS. If instead we try to protect integrity with if statements in the application, gaps appear as soon as new code paths are added. Declared in the DBMS, the constraint holds no matter which path the data arrives by.
INSERT adds tuples to a relation.
INSERT INTO customers (customer_id, name, address) VALUES ('C01', 'Tanaka Trading', 'Chiyoda-ku, Tokyo'), ('C02', 'Sato Industries', 'Kita-ku, Osaka');
INSERT INTO products (product_id, name, unit_price) VALUES ('P01', 'Bolt', 120), ('P02', 'Nut', 80);
INSERT INTO orders (order_id, order_date, customer_id) VALUES (1001, '2026-04-01', 'C01'), (1002, '2026-04-03', 'C02');
INSERT INTO order_items (order_id, product_id, quantity) VALUES (1001, 'P01', 10), (1001, 'P02', 25), (1002, 'P01', 4);SELECT combines selection (extracting the rows satisfying a condition, the of relational algebra) and projection (extracting columns, ).
SELECT product_id, quantityFROM order_itemsWHERE order_id = 1001;The result is , that is, the two rows ('P01', 10), ('P02', 25).
UPDATE and DELETE rewrite and remove existing rows.
UPDATE customers SET address = 'Nishi-ku, Yokohama' WHERE customer_id = 'C01';DELETE FROM order_items WHERE order_id = 1002 AND product_id = 'P01';Compare this with Example 1.1. The address update touches exactly one row of customers. Deleting an order does not delete a customer. The whole effect of normalization is concentrated in these two lines.
JOIN combines several relations under a condition. The inner join is defined as the set of tuples of satisfying the condition .
Example 3.1(Joining four tables and carrying the aggregation through to the end)
We compute the total amount of each order.
SELECT o.order_id, c.name AS customer, SUM(oi.quantity * p.unit_price) AS totalFROM orders oJOIN customers c ON c.customer_id = o.customer_idJOIN order_items oi ON oi.order_id = o.order_idJOIN products p ON p.product_id = oi.product_idGROUP BY o.order_id, c.nameORDER BY o.order_id;Let us follow the computation. Joining orders with order_items produces three rows (two for order 1001, one for 1002). Joining each of them with customers and products gives the following intermediate result.
| order_id | customer | product_id | quantity | unit_price | subtotal |
|---|---|---|---|---|---|
| 1001 | Tanaka Trading | P01 | 10 | 120 | 1200 |
| 1001 | Tanaka Trading | P02 | 25 | 80 | 2000 |
| 1002 | Sato Industries | P01 | 4 | 120 | 480 |
GROUP BY o.order_id, c.name groups by order number and SUM adds the subtotals.
| order_id | customer | total |
|---|---|---|
| 1001 | Tanaka Trading | 3200 |
| 1002 | Sato Industries | 480 |
Indeed , and . With the single table of Example 1.1 no join would have been needed, but that table carried update anomalies instead. Normalization is a trade in which safety at update time is bought with the cost of joins at query time.
The aggregation of Example 3.1 has a defect that cannot be ignored in practice. Because the unit price is taken from products, raising the price of a product also changes the total of past orders. “The current unit price of product P01 is 120” and “the unit price of P01 at the time of order 1001 was 120” are different facts. If the latter is needed, we give order_items a column unit_price_at_order. This looks like duplication, but the functional dependency holds only for the present, and the unit price at the time of the order is not determined by , so this is not a violation of any normal form. Normal forms are decided by the functional dependencies that actually hold, not by how the attributes look.
4. Functional dependencies and attribute closure
Section titled “4. Functional dependencies and attribute closure”To discuss normalization we must formalize the relationship “once the value of this column is fixed, the value of that column is fixed too”.
Definition 4.1(Functional dependency)
Let be a relation schema and . A relation over satisfies the functional dependency if
holds. When , the dependency holds for every ; such a dependency is called trivial.
Given a set of functional dependencies, a relation satisfying all of them is called -legal. If every -legal relation satisfies , we say that is logically implied by and write . We write for the set of all with .
Comparing with Definition 2.2, we see that being a superkey is nothing other than holding. Keys are thus a special case of functional dependencies.
For the order example we take the following basis of dependencies that hold.
Read out loud: “the order number determines the order date and the customer”, “the customer ID determines the customer’s name and address”, “the product ID determines the product name and unit price”, “the order number together with the product ID determines the quantity”. Each of these is a fact about the business, not something guessed by looking at the data. It is worth emphasizing that functional dependencies are specifications of the business, not properties that the current data happens to satisfy.
In general is enormous, but what we actually need is only “what is determined by a particular ”. That is what the attribute closure gives us.
Definition 4.2(Attribute closure)
For , define the attribute closure as the result of the following procedure.
- Set .
- As long as contains some with and , set .
- Stop when no such dependency remains.
Since is finite and grows strictly at each step, the procedure halts after at most additions.
Lemma 4.3(Attribute closure theorem)
For a relation schema , a set of functional dependencies, and ,
holds.
Proof(Lemma 4.3)
() Soundness. By induction on the number of additions in the construction of , we show that every -legal relation satisfies .
Initially , and is a trivial functional dependency, hence holds in every .
Induction step. Write for the current value and assume satisfies . Suppose the procedure uses (with ) to update to . Let satisfy . By the induction hypothesis . Since , it follows that , and since is -legal it satisfies , so . Hence , that is, satisfies .
The value of at termination is , so . If , then gives , so .
() Completeness. We argue by contraposition. Assume and construct a relation that is -legal yet fails to satisfy .
Pick . We may assume every domain contains (if not, pick two of its elements instead), and consider the relation consisting of the following two tuples.
Since we have , so and consists of exactly two tuples.
We show that is -legal. Take any and distinguish two cases.
- Case . The procedure of Definition 4.2 has halted, so (otherwise could add something more and it would not have halted). Hence both and are contained in , and there and both take the value and agree. Therefore is satisfied.
- Case . Take ; then , so . Since contains only and , there is no pair of distinct tuples agreeing on , and is satisfied vacuously.
On the other hand gives , while and give . So does not satisfy . Having constructed an -legal counterexample, we conclude .
Corollary 4.4(Test for superkeys)
is a superkey if and only if .
Proof(Corollary 4.4)
That is a superkey means that holds in every -legal relation, that is, . By Lemma 4.3 this is equivalent to , and since always holds, it is equivalent to .
Example 4.5(Finding the candidate keys of the order schema)
Let and .
We compute .
- .
- applies (since ), so .
- applies, so .
- applies, so .
- applies, so .
Hence is a superkey by Corollary 4.4. We check minimality. We have (it does not contain ) and . Neither reaches , so is a candidate key.
Moreover, the only dependency involving is , and the attribute , which appears only on a right-hand side, cannot belong to any candidate key. Also and appear on the right-hand side of no dependency, so they must belong to every superkey. Therefore is the unique candidate key. The prime attributes are and ; the remaining are non-prime.
5. Normalization: the first three normal forms
Section titled “5. Normalization: the first three normal forms”Definition 5.1(First, second and third normal forms)
Let a relation schema and a set of functional dependencies be given.
First normal form (1NF): the domain of every attribute consists of atomic values; that is, no cell contains a list of several values or a nested table.
Second normal form (2NF): is in 1NF and, for every candidate key , every non-prime attribute and every proper subset , we have . In other words, no non-prime attribute depends on merely part of a candidate key (there is no partial functional dependency).
Third normal form (3NF): is in 1NF and, for every nontrivial functional dependency in (where may be taken to be a single attribute), at least one of the following holds.
- is a superkey of , or
- is a prime attribute.
Allowing only dependencies satisfying condition 1 of 3NF (that is, refusing condition 2) gives Boyce–Codd normal form (BCNF). The inclusions among these are BCNF 3NF 2NF 1NF.
Why climb this staircase? The reason is entirely contained in the following proposition.
Proposition 5.2(A determinant that is not a key necessarily creates redundancy)
Consider a relation schema and a set of functional dependencies. Suppose and satisfy
- ( determines ), and
- is not a superkey of .
Then there exists an -legal relation containing two tuples with
That is, the single fact “when the value of is , the value of is ” can be stored redundantly in two rows.
Proof(Proposition 5.2)
Since is not a superkey, Corollary 4.4 gives , so we may pick .
Take the same two-tuple relation constructed in the completeness half of the proof of Lemma 4.3: is on all attributes, and is on and on . By the same case distinction, is -legal.
Since , we have . Since , we have . Finally, and Lemma 4.3 give , so . All three conditions are met.
Proposition 5.2 does not say “redundancy may occur”; it says “an instance containing the redundancy is perfectly legitimate under the business rules”. Being legitimate, it will appear sooner or later. And where there is redundancy, an update anomaly that rewrites only one of the copies becomes possible. The duplicated address in Example 1.1 was thus a consequence of not being a superkey of in (the only candidate key was ).
Conversely, if condition 1 of 3NF holds then is a superkey, so forces ; there are no duplicate rows and this form of redundancy does not arise. That 3NF admits condition 2 ( prime) as an exception is a compromise, and the corresponding redundancy remains. BCNF refuses this compromise, but in exchange there are cases in which only decompositions that fail to preserve dependencies exist (see the remark following Theorem 6.4).
flowchart TB A["Unnormalized<br/>multi-valued cells, repeating columns"] -->|"atomize values, expand repetitions into rows"| B["First normal form (1NF)"] B -->|"split off dependencies on part of a candidate key"| C["Second normal form (2NF)"] C -->|"split off transitive dependencies through non-prime attributes"| D["Third normal form (3NF)"] D -->|"forbid dependencies on prime attributes as well"| E["Boyce-Codd normal form (BCNF)"]
Example 5.3(Decomposing the order schema from 1NF to 3NF)
We start from of Example 1.1 with . As computed in Example 4.5, the only candidate key is .
Checking 1NF. If the original data had been in a format writing two entries as P01:10, P02:25 in a product column, it would violate 1NF. In that case, expanding one order-and-product pair per row yields 1NF. The table of Example 1.1 is already in this shape, so it is in 1NF.
Decomposition to 2NF. For the non-prime attributes we look for dependencies on proper subsets of the candidate key .
- and hold, and . Moreover gives and as well. These are partial functional dependencies.
- and hold, and . These too are partial functional dependencies.
So we carve out the group of attributes determined by and the group determined by .
The candidate key of is , its only non-prime attribute is , and neither nor holds ( belongs to neither nor ), so is in 2NF. The candidate key of is , whose only proper subset is the empty set, and does not hold, so is in 2NF. The candidate key of is , and by the same argument is in 2NF.
Decomposition to 3NF. Examine . Its candidate key is , and the nontrivial dependency lies in . Since , the set is not a superkey of , and is not a prime attribute of either (the only candidate key of is ). Hence 3NF is violated. The cause is the transitive dependency . We carve out the part determined by .
The final decomposition consists of the four schemas
which correspond to orders, customers, products and order_items of §3. Let us verify the 3NF condition in each. In the only nontrivial dependency is and is the candidate key; in it is with the candidate key; in it is with the candidate key; in it is with the candidate key. All satisfy condition 1 of 3NF, so in fact they are in BCNF as well.
erDiagram
CUSTOMERS ||--o{ ORDERS : "places"
ORDERS ||--|{ ORDER_ITEMS : "contains"
PRODUCTS ||--o{ ORDER_ITEMS : "is specified in"
CUSTOMERS {
char customer_id PK
varchar name
varchar address
}
ORDERS {
int order_id PK
date order_date
char customer_id FK
}
PRODUCTS {
char product_id PK
varchar name
int unit_price
}
ORDER_ITEMS {
int order_id PK_FK
char product_id PK_FK
int quantity
}Normalization is a means, not an end. After decomposing to 3NF, one sometimes deliberately duplicates data into a separate table for places that are read extremely often and almost never updated (precomputed monthly sales, say). This is called denormalization. There is exactly one condition for doing it: make explicit who is responsible for keeping the redundant copies in sync. Synchronize mechanically with a trigger, a materialized view or a batch recomputation, and do not saddle the application’s write path with the obligation to “update two places”. As Proposition 5.2 shows, manual synchronization eventually breaks.
6. Correctness of a decomposition: lossless joins
Section titled “6. Correctness of a decomposition: lossless joins”In Example 5.3 we split tables apart. But depending on how we split, the original information can be lost. We now state the condition that rules this danger out.
Definition 6.1(Lossless-join decomposition)
Let be a relation schema and subsets with . Relative to a set of functional dependencies, this decomposition is a lossless-join decomposition if, for every -legal relation ,
holds. Here is the natural join, which joins tuples agreeing on the common attributes .
Example 6.2(A decomposition that loses information)
Let with no functional dependencies at all (), and take . Decomposing into and gives
Since the only value of on either side is , the natural join produces tuples.
The tuples and , absent from the original , have been created. Decomposing and rejoining manufactured false data. Since always holds for any decomposition, what is lost is not rows but the linkage information “which value sat in the same row as which”.
Theorem 6.3(Heath's theorem)
Partition a relation schema into three pairwise disjoint subsets (so and ), and suppose the set of functional dependencies satisfies . Then the decomposition into and is a lossless-join decomposition; that is, for every -legal relation ,
holds.
Proof(Theorem 6.3)
Note that (since , the intersection is exactly ).
() Let . Then and , and both agree on with the value . So by the definition of the natural join these two are joined, and the result is the tuple taking on , on and on — that is, itself. Hence . This direction does not use the functional dependency.
() Let be a tuple of the right-hand side. By the definition of the natural join there exist with
In particular . Now the hypothesis together with the -legality of implies that satisfies . Since , it follows that .
Then takes on , on , and on . As , the tuple agrees with on all attributes, so .
The reason Example 6.2 failed is precisely that neither nor held. Every step of the decomposition in Example 5.3 has the shape of Theorem 6.3. For instance, when was split into and , taking , , makes hold, so the split is lossless. If we split so that the shared column of the part carved out is a determinant of what is carved out, no information is lost. This is the justification of the procedure “collect the attributes determined by into a separate table”.
There is one more desirable property of a decomposition: dependency preservation, meaning that the constraints of the original can be checked using only the constraints on the individual tables of the decomposition. When this fails, a state may be allowed in which each table is correct on its own yet the whole violates the business rule, and checking the rule requires a join.
Theorem 6.4(3NF synthesis theorem)
For every relation schema and every set of functional dependencies there exists a decomposition of satisfying all three of the following conditions, and it can be constructed in time polynomial in the size of .
- Each is in 3NF (with respect to the projection of onto ).
- The decomposition is a lossless-join decomposition.
- The decomposition is dependency preserving.
The Appendix sketches the construction (the 3NF synthesis algorithm). A complete proof can be found in Chapter 11 of Abiteboul–Hull–Vianu, Foundations of Databases, or in Chapter 7 of Ullman, Principles of Database and Knowledge-Base Systems, Volume I.
For BCNF the situation is different. A lossless decomposition is always available, but dependency preservation cannot be achieved in general. The standard counterexample is with . The candidate keys are and ; since the of is not a superkey, BCNF is violated, whereas is prime, so 3NF is satisfied. No matter how this is decomposed into BCNF, never fits inside a single table and cannot be preserved. That 3NF is the practical standard comes from exactly this position: it is the strongest normal form for which the three conditions can be met simultaneously.
7. Differences from NoSQL, and how to choose
Section titled “7. Differences from NoSQL, and how to choose”In the late 2000s, as the scale of web services outgrew what a single server could handle, data models other than the relational one came into wide use. Collectively they are called NoSQL. Here are four representative families.
| Family | Data model | Representative systems | Strengths |
|---|---|---|---|
| Key-value (KVS) | map from keys to opaque values | Redis, Amazon DynamoDB | extremely fast reads and writes by key; sessions, caches |
| Document | map from keys to nested JSON-like documents | MongoDB, Couchbase | reading and writing a whole aggregate at once; data with a fluid schema |
| Wide column | row key + column families | Apache Cassandra, HBase | time series and logs with extremely high write volume |
| Graph | vertices and edges | Neo4j | multi-hop traversal (friends of friends, path finding) |
The essential differences from a relational database can be organized into three points.
First, when the join happens. The relational model stores facts split into their smallest units and assembles them with JOIN at the moment they are needed (Example 3.1). The document model does the opposite: following the principle write together what you read together, it stores the data pre-assembled.
Example 7.1(The same order in a document model)
Order 1001, which Example 5.3 split across four tables, becomes a single document in a document model.
{ "_id": 1001, "order_date": "2026-04-01", "customer": { "id": "C01", "name": "Tanaka Trading", "address": "Chiyoda-ku, Tokyo" }, "items": [ { "product_id": "P01", "name": "Bolt", "unit_price": 120, "quantity": 10 }, { "product_id": "P02", "name": "Nut", "unit_price": 80, "quantity": 25 } ]}The benefit is clear. The operation “display the contents of order 1001” is a single read by the key 1001. No join is needed, and even if the data is spread over several servers this document is guaranteed to live on one of them.
The cost is equally clear. The customer name is replicated in every order, which is exactly the redundancy pointed out by Proposition 5.2. If Tanaka Trading changes its company name, every order document of that customer must be rewritten. The document model is a design that accepts this cost knowingly. It is acceptable only in businesses that want to preserve the customer name as of the time of the order (the same reasoning as in Remark 3.2), or in which name changes effectively never happen.
Note also that items being an array violates the 1NF of Definition 5.1. One may say that the document model is a model that deliberately abandons 1NF.
Second, the ease of horizontal partitioning (sharding). If the counterpart of a join lives on another server, data must be gathered across the network, and each gathering adds a round-trip delay (decomposition of end-to-end delay(Proposition 2.2)[ネットワーク(TCP/IP)]). Document stores and key-value stores can be partitioned mechanically by hashing the key, and since every operation completes on a single machine, performance scales almost linearly with the number of machines (for the response time when load is spread evenly over machines, see Corollary 5.3[クラウドコンピューティング]). Relational databases can be partitioned too, but joins and transactions crossing partitions form the barrier.
Third, the consistency guarantee. Relational databases provide transactions satisfying ACID (atomicity, consistency, isolation, durability), so that “debit account A and credit account B” can be executed indivisibly. In distributed systems there is a constraint known as E. Brewer’s CAP theorem: while a network partition is in effect, strong consistency and availability cannot both be satisfied at once. Most NoSQL systems choose availability (Definition 6.1[クラウドコンピューティング]) in this situation and offer eventual consistency (once the partition heals, the values converge over time). Applications must be written on the assumption that reading a different node immediately after a write returns may show a stale value.
The following summarizes the rules of thumb.
| Situation | Recommendation |
|---|---|
| Consistency across several entities is required (stock and orders, transfers between accounts) | relational database |
| The shape of the queries is not fixed in advance (analytics, filtering in an admin console) | relational database |
| Always read and write by a single key, with tight latency requirements (sessions, carts) | KVS |
| The aggregate boundary is clear and the whole aggregate is read and written as a unit | document store |
| Write volume greatly exceeds read volume and data is appended in time order | wide column store |
| Multi-hop relationships are traversed (recommendation, routing, inheritance of permissions) | graph store |
Today the boundaries are blurring. PostgreSQL’s JSONB type stores documents in a column and can index them, and many NoSQL systems have partially introduced transactions. I think the safe order is to design first with a normalized relational model and replace with a different tool only the places where measurement shows a problem. Choosing a database is a judgement that includes operations (backup, monitoring, failover), so consider it together with the managed-service options (service models(Definition 2.2)[クラウドコンピューティング]) discussed in Cloud Computing. How much availability improves when a standby is provisioned can be estimated in the form of Proposition 6.2[クラウドコンピューティング]. The standard practice is to manage the schema definition itself in files, like code, and keep the history of migrations (commit graph(Definition 4.1)[Git]) in a version control system (Git). To understand the behaviour of distributed databases one needs to know the reality of network latency and partitions, so Networking (TCP/IP) is a useful companion.
8. Exercises
Section titled “8. Exercises”Exercise 8.1Standard
Let and .
- Find all candidate keys of .
- Is in 3NF? In BCNF? Give reasons.
Solution
1. We compute attribute closures.
: by we get , by we get , by we get . So is a superkey, and its only proper subset is the empty set (), so it is a candidate key.
: by we get , and from there we reach as in the case of . So is a candidate key too.
, , , none of which reaches .
: by we get , by we get , by we get . Since neither nor is a superkey, is a candidate key.
: by we get , by we get , by we get . Since neither nor is a superkey, is also a candidate key.
, so this is not a candidate key. Altogether the candidate keys are the four sets , , and .
2. Count the prime attributes. Each of occurs in some candidate key, so every attribute is prime. Therefore the second condition of 3NF in Definition 5.1 always holds, and is in 3NF.
It is not in BCNF. The dependency is nontrivial, and , so is not a superkey by Corollary 4.4. BCNF admits only condition 1 (the determinant is a superkey), so this is a violation. As stated in Remark 6.5, the difference lies in the compromise “whether redundancy on prime attributes is tolerated”.
Exercise 8.2Standard
As a university enrolment record, consider with functional dependencies
(one course has exactly one teacher; one teacher may hold several courses). Find the candidate keys, point out a violation of 2NF and a violation of 3NF, and decompose down to 3NF. State also why each decomposition is lossless.
Solution
Abbreviate the symbols as . Then .
Candidate keys. : adds , adds and , adds , adds , giving all of . We have and , neither of which reaches . Moreover and occur on the right-hand side of no dependency, so they belong to every superkey. Hence the only candidate key is , and the prime attributes are and .
Violation of 2NF. The non-prime attribute depends on the proper subset of the candidate key (). Likewise depend on (, , and ). All of these are partial functional dependencies.
Violation of 3NF. Even after resolving the 2NF violation to obtain , the candidate key is , and in we have , which is not the whole attribute set of this table, so is not a superkey. Since is not prime either, 3NF is violated (the transitive dependency ).
Decomposition.
The only nontrivial dependency in each table is respectively , , , , and in each the left-hand side is the candidate key of that table, so all are in 3NF (indeed in BCNF).
Losslessness. Every split has the shape of Theorem 6.3. For instance, at the stage where is carved out of , taking , , makes hold, so the split is lossless. At the stage where is carved out of , taking , , makes hold, so that split is lossless as well. The result of repeating lossless decompositions is lossless, so the whole is lossless.
Exercise 8.3Easy
For the schema of §3, write SQL that lists the cumulative purchase amount per customer in decreasing order of amount, including customers who have bought nothing at all with an amount of .
Solution
Since customers who have bought nothing must also be included, we need an outer join anchored on customers.
SELECT c.customer_id, c.name, COALESCE(SUM(oi.quantity * p.unit_price), 0) AS totalFROM customers cLEFT JOIN orders o ON o.customer_id = c.customer_idLEFT JOIN order_items oi ON oi.order_id = o.order_idLEFT JOIN products p ON p.product_id = oi.product_idGROUP BY c.customer_id, c.nameORDER BY total DESC;Making the inner joins plain JOIN (inner joins) would delete the rows of customers without orders and fail the requirement. Also, for a customer with no orders every argument of SUM is NULL and SUM returns NULL, so we replace it by with COALESCE. On the data of §3, C01 gives and C02 gives .
Exercise 8.4Hard
Let and . Show that the decomposition , is not a lossless-join decomposition by explicitly constructing an -legal relation as a counterexample. Then give one decomposition that is lossless, with a justification.
Solution
Counterexample. Take (in the order ). The values of are distinct, so there is no pair for which imposes anything, and is -legal.
We have and . Since the only value of on either side is , the natural join returns the tuples
which include and , absent from the original . Hence the decomposition is not lossless. The cause is that the common attribute determines neither nor : neither nor is implied by (indeed ).
A lossless decomposition. Take and . Setting , , , the sets are pairwise disjoint with union , and . All hypotheses of Theorem 6.3 are met, so this decomposition is a lossless-join decomposition.
As a check, apply it to the above. We get and , and joining on yields exactly the two tuples and , which agree with .
References
Section titled “References”- E. F. Codd, “A Relational Model of Data for Large Shared Data Banks”, Communications of the ACM 13 (1970), 377–387. The original paper proposing the relational model and normalization. doi:10.1145/362384.362685
- S. Abiteboul, R. Hull, V. Vianu, Foundations of Databases, Addison-Wesley, 1995 — Chapter 8 (functional dependencies) and Chapter 11 (normal forms and decomposition). A rigorous treatment of reasoning about functional dependencies and of the theory of normal forms. The full text is made available by the authors at webdam.inria.fr/Alice/.
- A. Silberschatz, H. F. Korth, S. Sudarshan, Database System Concepts, 7th ed., McGraw-Hill, 2019 — Chapter 7 (normalization) and Chapters 3–4 (SQL). The standard undergraduate textbook.
- J. D. Ullman, Principles of Database and Knowledge-Base Systems, Volume I, Computer Science Press, 1988 — Chapter 7. Contains the construction of the 3NF synthesis algorithm and the proof of its correctness.
- M. Kleppmann, Designing Data-Intensive Applications, O’Reilly, 2017 — Chapter 2 (data models and query languages), Chapters 5–9 (replication, partitioning, transactions, consistency). Compares the relational model with NoSQL and treats consistency in distributed environments.
- S. Gilbert, N. Lynch, “Brewer’s Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services”, ACM SIGACT News 33 (2002), 51–59. The formal proof of the CAP theorem. doi:10.1145/564585.564601
Appendix: Outline of the 3NF synthesis algorithm
Section titled “Appendix: Outline of the 3NF synthesis algorithm”We describe the procedure that produces the decomposition of Theorem 6.4. The details of the proof are in the references listed in Remark 6.5.
Stage 1: build a minimal cover. From the set of functional dependencies, build (preserving ) satisfying the following three conditions. (a) The right-hand side of every dependency is a single attribute. (b) For every dependency , removing any attribute from destroys equivalence with (the left-hand side is minimal). (c) Removing any dependency destroys equivalence with (the number of dependencies is minimal). The procedure is: first split the right-hand sides into single attributes; then, for each dependency, tentatively remove each attribute of the left-hand side and test derivability using Lemma 4.3; finally, tentatively remove each dependency and run the same test. Every test is a computation of an attribute closure, so this runs in polynomial time.
Stage 2: make one table per left-hand side. Group the dependencies of by identical left-hand sides, and for a left-hand side with right-hand sides take as one relation schema. At this stage every dependency of fits inside some table, which guarantees dependency preservation.
Stage 3: add one candidate key. If none of the tables built in stage 2 contains a candidate key of , choose a candidate key and add a table whose attribute set is itself. This guarantees the lossless join (it makes the order of joins traceable from the candidate key).
Stage 4: remove contained tables. When the attribute set of one table is contained in that of another, discard the smaller one.
Applying this procedure to the order schema of Example 5.3, the set is already close to a minimal cover, and grouping by left-hand side yields the four schemas , , , . The last of these contains the candidate key , so the addition of stage 3 is unnecessary. The result agrees with the decomposition we carried out by hand.
Limits of this procedure. The synthesis algorithm guarantees only 3NF. Whether each table is further in BCNF must be checked individually, and forcing a decomposition when it is not can destroy dependency preservation. In practice I think the realistic approach is to decompose to 3NF and then judge, from the business side, whether the remaining exceptions (dependencies on prime attributes) actually cause trouble.
Report an error in this article ・Operated by: Mugen Giken LLC ・Pricing ・Terms ・Legal notice
© 2026 夢現技研合同会社 ・Feeding the text to an LLM is welcome. Code samples are MIT licensed.