Optimization Strategy: Denormalization
When and how to denormalize a relational schema for performance, and the trade-offs involved.
Contents
- The meaning of denormalization
- When to denormalize to improve performance
Examples use the DreamHome case study (Connolly & Begg, Ch. 18).
Why denormalize?
- The result of normalization is a design that is structurally consistent with minimal redundancy.
- However, a normalized database does not always provide maximum processing efficiency — many queries need several joins.
- It may be necessary to accept the loss of some benefits of a fully normalized design in favour of performance.
Also consider that denormalization:
- makes implementation more complex;
- often sacrifices flexibility;
- may speed up retrievals but slows down updates.
What is denormalization?
Denormalization is a refinement to the relational schema such that the degree of normalization for a modified relation is less than the degree of at least one of the original relations.
The term is also used more loosely for combining two relations into one new relation that is still normalized but contains more nulls than the originals.
When to apply denormalization
Consider it specifically to speed up frequent or critical transactions, in these situations:
- Combining 1:1 relationships
- Duplicating non-key attributes in 1:* relationships to reduce joins
- Duplicating foreign key attributes in 1:* relationships to reduce joins
- Introducing repeating groups
- Partitioning relations
Denormalize only after measuring a real performance problem on a critical transaction, and document it so that the redundant data is kept consistent (e.g. with triggers — Week 6).
Sample relations (DreamHome)
The relation diagram links: Client —Attends— Interview; Client —Requests— Viewing; PropertyForRent —Takes— Viewing; PrivateOwner —POwns— PropertyForRent; Branch —Offers— PropertyForRent; Branch —Provides— Telephone.
PropertyForRent (normalized)
| propertyNo | street | city | postcode | type | rooms | rent | ownerNo | staffNo | branchNo |
|---|---|---|---|---|---|---|---|---|---|
| PA14 | 16 Holhead | Aberdeen | AB7 5SU | House | 6 | 650 | CO46 | SA9 | B007 |
| PL94 | 6 Argyll St | London | NW2 | Flat | 4 | 400 | CO87 | SL41 | B005 |
| PG4 | 6 Lawrence St | Glasgow | G11 9QX | Flat | 3 | 350 | CO40 | B003 | |
| PG36 | 2 Manor Rd | Glasgow | G32 4QX | Flat | 3 | 375 | CO93 | SG37 | B003 |
| PG21 | 18 Dale Rd | Glasgow | G12 | House | 5 | 600 | CO87 | SG37 | B003 |
| PG16 | 5 Novar Dr | Glasgow | G12 9AX | Flat | 4 | 450 | CO93 | SG14 | B003 |
PrivateOwner
| ownerNo | fName | lName | address | telNo |
|---|---|---|---|---|
| CO46 | Joe | Keogh | 2 Fergus Dr, Aberdeen AB2 7SX | 01224-861212 |
| CO87 | Carol | Farrel | 6 Achray St, Glasgow G32 9DX | 0141-357-7419 |
| CO40 | Tina | Murphy | 63 Well St, Glasgow G42 | 0141-943-1728 |
| CO93 | Tony | Shaw | 12 Park Pl, Glasgow G4 0QR | 0141-225-7025 |
1. Combining 1:1 relationships
Client and Interview have a 1:1 relationship (a client attends at most one interview). If they are frequently accessed together, combine them into one relation ClientInterview.
| Client | |||
|---|---|---|---|
| clientNo | fName | lName | maxRent |
| CR76 | John | Kay | 425 |
| CR56 | Aline | Stewart | 350 |
| CR74 | Mike | Ritchie | 750 |
| CR62 | Mary | Tregear | 600 |
| Interview | |||
|---|---|---|---|
| clientNo | staffNo | dateInterview | comment |
| CR56 | SG37 | 11-Apr-00 | current lease ends in June |
| CR62 | SA9 | 7-Mar-00 | needs property urgently |
Combined:
| clientNo | fName | lName | telNo | prefType | maxRent | staffNo | dateInterview | comment |
|---|---|---|---|---|---|---|---|---|
| CR76 | John | Kay | 0207-774-5632 | Flat | 425 | |||
| CR56 | Aline | Stewart | 0141-848-1825 | Flat | 350 | SG37 | 11-Apr-03 | current lease ends in June |
| CR74 | Mike | Ritchie | 01475-392178 | House | 750 | |||
| CR62 | Mary | Tregear | 01224-196720 | Flat | 600 | SA9 | 7-Mar-03 | needs property urgently |
No join needed any more — but clients with no interview now have nulls in the interview columns. This is the “loose” form of denormalization (still normalized, more nulls).
Worth it only if the participation is fairly high (few nulls) and the two are almost always read together.
2. Duplicating non-key attributes in 1:* relationships
A frequent query lists properties with their owner’s last name:
SELECT p.*, o.lName
FROM PropertyForRent p, PrivateOwner o
WHERE p.ownerNo = o.ownerNo AND branchNo = 'B003';
If we duplicate lName from PrivateOwner into PropertyForRent, the join disappears:
| propertyNo | … | rent | ownerNo | lName | staffNo | branchNo |
|---|---|---|---|---|---|---|
| PA14 | … | 650 | CO46 | Keogh | SA9 | B007 |
| PL94 | … | 400 | CO87 | Farrel | SL41 | B005 |
| PG4 | … | 350 | CO40 | Murphy | B003 | |
| PG36 | … | 375 | CO93 | Shaw | SG37 | B003 |
| PG21 | … | 600 | CO87 | Farrel | SG37 | B003 |
| PG16 | … | 450 | CO93 | Shaw | SG14 | B003 |
SELECT p.*
FROM PropertyForRent p
WHERE branchNo = 'B003';
If an owner’s last name changes, it must be updated in every property they own (e.g. Farrel appears twice) — an update anomaly reintroduced on purpose. Extra storage is also used.
Special case: lookup tables
Lookup (reference/code) tables hold a code and its description, e.g. PropertyType(type, description) with H = House, F = Flat, and PropertyForRent stores only the code:
| PropertyType | |
|---|---|
| type | description |
| H | House |
| F | Flat |
Benefits of the lookup table: reduces the size of the child relation (a 1-char code instead of a description), makes changing a description easy (one row), and can be used to validate input.
If the lookup table is used frequently or critically, and the description rarely changes, duplicate the description in the child relation so no join is needed:
| propertyNo | street | city | type | description | rooms | rent |
|---|---|---|---|---|---|---|
| PA14 | 16 Holhead | Aberdeen | H | House | 6 | 650 |
| PL94 | 6 Argyll St | London | F | Flat | 4 | 400 |
| PG4 | 6 Lawrence St | Glasgow | F | Flat | 3 | 350 |
| … | … | … | … | … | … | … |
Figure 18.5 — Modified PropertyForRent with duplicated description. The original lookup table can be kept for validation.
3. Duplicating foreign key attributes in 1:* relationships
Original: PrivateOwner —POwns→ PropertyForRent ←Offers— Branch. A frequent query: list all private owners at a branch:
SELECT o.lName
FROM PropertyForRent p, PrivateOwner o
WHERE p.ownerNo = o.ownerNo AND branchNo = 'B003';
Remove the join by duplicating the foreign key branchNo into PrivateOwner — i.e. introducing a direct relationship Branch —Serves→ PrivateOwner:
SELECT o.lName
FROM PrivateOwner o
WHERE branchNo = 'B003';
| ownerNo | fName | lName | address | telNo | branchNo |
|---|---|---|---|---|---|
| CO46 | Joe | Keogh | 2 Fergus Dr, Aberdeen AB2 7SX | 01224-861212 | B007 |
| CO87 | Carol | Farrel | 6 Achray St, Glasgow G32 9DX | 0141-357-7419 | B003 |
| CO40 | Tina | Murphy | 63 Well St, Glasgow G42 | 0141-943-1728 | B003 |
| CO93 | Tony | Shaw | 12 Park Pl, Glasgow G4 0QR | 0141-225-7025 | B003 |
This only works if an owner rents properties through a single branch. If an owner could use many branches, you’d need a *:* relationship between Branch and PrivateOwner instead.
PropertyForRent holds branchNo because a property may not yet have a staff member. If it didn’t, the query would need two joins (via Staff):
SELECT o.lName
FROM Staff s, PropertyForRent p, PrivateOwner o
WHERE s.staffNo = p.staffNo AND p.ownerNo = o.ownerNo
AND s.branchNo = 'B003';
Removing two joins gives even greater justification for duplicating branchNo into PrivateOwner.
4. Introducing repeating groups
Repeating groups are removed during normalization (1NF) into a separate relation — e.g. a branch’s telephone numbers are held in Telephone(telNo, branchNo). If access to the phone numbers is critical, fold them back into Branch as separate columns:
| branchNo | street | city | postcode | telNo1 | telNo2 | telNo3 |
|---|---|---|---|---|---|---|
| B005 | 22 Deer Rd | London | SW1 4EH | 0207-886-1212 | 0207-886-1300 | 0207-886-4100 |
| B007 | 16 Argyll St | Aberdeen | AB2 3SU | 01224-67125 | ||
| B003 | 163 Main St | Glasgow | G11 9QX | 0141-339-2178 | 0141-339-4439 | |
| B004 | 32 Manse Rd | Bristol | BS99 1NZ | 0117-916-1170 | ||
| B002 | 56 Clover Dr | London | NW10 6EU | 0208-963-1030 |
telNo1 can be marked as an alternate key (AK). Consider this only when:
- the absolute number of items in the repeating group is known (here, a maximum of three);
- the number is static and will not change over time;
- the number is not very large — typically not more than 10 (less important than the first two).
5. Partitioning relations
Rather than combining relations, decompose them into smaller, more manageable partitions. Two main types:
Horizontal partitioning
Distributes the tuples (rows) across smaller relations.
e.g. split PropertyForRent by type (Houses vs Flats), or split a sales table by year so queries on the current year scan less data.
Vertical partitioning
Distributes the attributes (columns) across smaller relations; the primary key is duplicated in each so the original can be rebuilt with a join.
e.g. separate rarely-used large columns (photos, long descriptions) from frequently-used ones.
Benefits: improved load balancing, better performance (smaller scans), higher availability (one partition can be offline), improved recovery, security (restrict access by partition). Costs: complexity, and queries across partitions are slower.
Advantages and disadvantages
| Advantages | Disadvantages |
|---|---|
Can improve performance by:
|
|
Table 18.1 — Advantages and disadvantages of denormalization.