In lesson 01-01 we used words like table, row, column, primary key and foreign key in a deliberately informal way: they were enough to diagnose BiblioRed's spreadsheet without having to define anything rigorously. Now it is time to do it properly. The relational model is not "a tidy way of organizing tables": it is a mathematical theory, published by Edgar F. Codd in 1970, and that formal foundation is exactly why relational systems have gone half a century without being displaced.
Understanding the model has a very practical consequence. When, two lessons from now, you write a JOIN and it does not return what you expected, or when a comparison against NULL filters out every row, the explanation will not be in the PostgreSQL manual: it will be in the theory we cover today. This is the last lesson without typing (almost: there is illustrative SQL, but you will not run it yet). By the end you will have the complete blueprint of the BiblioRed schema, which in the next lesson we will turn into real tables inside biblioredb.
Contents
- Relation, tuple, attribute and domain
- Degree and cardinality
- Why a relation is a set
- Relation versus spreadsheet
- The key family: superkey, candidate, primary, alternate
- Foreign keys and the link between relations
- Natural key versus surrogate key: the ISBN case
- The model's three integrity rules
- The
NULLvalue and three-valued logic - Introduction to relational algebra
- The conceptual schema of BiblioRed
- Common mistakes and tips
- Exercises
- Conclusion
- Relation, tuple, attribute and domain
The relational model is built on four concepts. We present each with its formal name and its everyday name, because in daily work you will hear both.
| Formal name | Everyday name (SQL) | What it is |
|---|---|---|
| Relation | Table | A set of tuples that share the same structure |
| Tuple | Row or record | One element of the relation: the data for one member, one book |
| Attribute | Column or field | A named property, present in every tuple |
| Domain | Data type | The set of valid values for an attribute |
Beware of a very widespread confusion: "relation" does not mean "link between tables". In the relational model, a relation is a table. The link between tables is called a foreign key (or, in design terms, a relationship). The model is called "relational" not because tables relate to one another, but because each table is, mathematically, a relation in the set-theory sense: a subset of the Cartesian product of its domains.
Let's look at it with BiblioRed's members relation:
| member_id | first_name | last_name | join_date | branch_id | |
|---|---|---|---|---|---|
| 14 | Marta | Alsina | marta.alsina@example.org | 2021-03-08 | 2 |
| 15 | Iván | Pereda | ivan.pereda@example.org | 2021-09-19 | 2 |
| 16 | Nuria | Bastos | nuria.bastos@example.org | 2022-01-30 | 1 |
- The relation is
members. - Each of the three lines is a tuple.
first_name,email,join_date… are attributes.- The domain of
join_dateis "valid calendar dates"; that ofmember_id, "positive integers"; that ofemail, "strings of up to 120 characters". The domain is what stops"last Tuesday"from showing up injoin_date.
Schema and instance, once again
In 01-01 we distinguished schema (the structure) from instance (the data at a given moment). Formally:
- The relation schema is written
members(member_id, first_name, last_name, email, join_date, branch_id). It is stable: it changes only when the designer changes it. - The instance or extension is the concrete set of tuples present right now. It changes with every loan, every sign-up and every cancellation.
When we talk about "the members table" we almost always mean the schema; when we say "there are 12,000 members", we mean the instance.
- Degree and cardinality
Two elementary measurements of a relation:
- Degree (or arity): the number of attributes. The
membersrelation in the example has degree 6. Degree is a property of the schema. - Cardinality: the number of tuples. In the example, 3; in the real BiblioRed, 12,000. Cardinality is a property of the instance.
A handy mnemonic: degree is counted across and hardly ever changes; cardinality is counted down and changes all the time.
Edge cases worth having clear:
- A relation with cardinality 0 (no tuples) is perfectly valid: it is exactly what we have right now in
biblioredb, where the tables do not even exist yet, and what we will have after creating them in the next lesson. - A relation with degree 0 is a mathematical curiosity with no practical use; SQL does not allow it.
- Why a relation is a set
Here is the idea with the most consequences. A relation is a set of tuples, and mathematical sets have two properties that spreadsheets do not:
There is no order
In a set, {a, b, c} and {c, a, b} are the same set. Therefore, the rows of a table have no intrinsic order. Neither do the columns, even though SQL declares them in a sequence.
The practical consequence is blunt: if you run a query without ORDER BY, the manager can return the rows in any order, and that order can change between two identical runs without anyone having touched anything. It is not a bug: it is the model. If order matters, you have to ask for it explicitly (we will see this in 02-03).
There are no duplicates
In a set, an element either is or is not there; it cannot be there "twice". Therefore, a relation cannot contain two identical tuples. This property is what justifies the mandatory existence of a primary key: there must always be something that tells one tuple from another.
The small print: SQL is not that strict
Intellectual honesty: SQL does not implement the pure relational model. An SQL table is technically a multiset (bag), not a set: if you declare neither a primary key nor a uniqueness constraint, SQL will let you insert two identical rows and both will coexist.
-- Legal in SQL if the table has no primary key.
-- Forbidden in the pure relational model.
INSERT INTO members_no_key VALUES ('Marta', 'Alsina');
INSERT INTO members_no_key VALUES ('Marta', 'Alsina');
-- Result: two indistinguishable rows, impossible to delete separatelyThat is why one of the course's first practical rules is: every table gets a primary key. It is not bureaucracy; it is what restores the table's status as a relation.
- Relation versus spreadsheet
The BiblioRed spreadsheet we diagnosed in 01-01 looked a lot like a table. These are the differences that made it fail:
| Aspect | Relation (relational model) | Spreadsheet |
|---|---|---|
| Row order | Does not exist; requested with ORDER BY |
Intrinsic: row 7 sits between 6 and 8 |
| Duplicates | Forbidden (primary key) | Allowed and frequent |
| Domain of a column | Fixed and enforced by the manager | Each cell can hold whatever type it likes |
| Empty cells | NULL, with defined semantics |
Ambiguous blank: zero, empty text, not applicable? |
| Identifying a row | By the value of its key | By its position (A7) |
| References between data | Foreign keys, verified | Fragile formulas that break when rows are inserted |
| Simultaneous access | Controlled by the manager | One user at a time, or version conflicts |
The decisive difference is the second-to-last line. In a spreadsheet, a row is identified by where it is; in a relation, by what it is worth. That is why inserting a row in the middle of a sheet breaks formulas, and why in a database it breaks nothing: nobody depends on position.
- The key family: superkey, candidate, primary, alternate
To guarantee there are no duplicate tuples we need to identify each tuple. The model defines a precise hierarchy of concepts. We will work on BiblioRed's books relation:
| book_id | isbn | title | author_id | publisher | publication_year |
|---|---|---|---|---|---|
| 331 | 9788401339097 | The Map of Time | 1 | Editorial Andana | 2008 |
| 332 | 9788401337208 | The Pillars of the Earth | 2 | Editorial Andana | 1989 |
| 333 | 9788412007701 | The House of Tides | 3 | Ediciones Marlia | 2015 |
Superkey
A set of attributes that uniquely identifies each tuple: there cannot be two tuples with the same values in all of them.
In books, these are superkeys, among others:
{book_id}{isbn}{book_id, title}{isbn, publisher, publication_year}- The set of all attributes (it is always a superkey, because there are no duplicate tuples)
Notice that you can add irrelevant attributes to a superkey and it remains a superkey. That is why we need to sharpen the definition.
Candidate key
A minimal superkey: if you remove any attribute, it stops identifying uniquely. {book_id, title} is not a candidate, because {book_id} already suffices on its own. In books the candidate keys are:
{book_id}{isbn}
A relation can have several candidate keys, and it always has at least one.
Primary key
The candidate key that the designer chooses as the official identifier. It is the one that foreign keys in other tables will use and the one the manager relies on to organize storage. In books we will choose book_id (section 7 justifies why).
Alternate key
The candidate keys that were not chosen. In books, isbn is an alternate key. In SQL they are declared with UNIQUE, and they are just as mandatory to respect as the primary key: if you let two books share an ISBN, the BiblioRed catalog is corrupted all the same.
Composite key
A key (candidate or primary) made up of more than one attribute. If BiblioRed had a books_authors table for books written by several hands, its natural primary key would be {book_id, author_id}: neither the book alone nor the author alone is enough, but the pair is.
flowchart TD
A["All sets of attributes"] --> B["Superkeys<br/>identify uniquely"]
B --> C["Candidate keys<br/>minimal superkeys"]
C --> D["Primary key<br/>the chosen candidate"]
C --> E["Alternate keys<br/>the candidates not chosen<br/>(UNIQUE in SQL)"]
- Foreign keys and the link between relations
A foreign key is an attribute, or set of attributes, of one relation whose values must match those of the primary key of another relation (or of the same one).
In BiblioRed:
members.branch_idis a foreign key tobranches.branch_id: every member belongs to an existing branch.copies.book_idis a foreign key tobooks.book_id: every physical copy is a copy of a book in the catalog.loans.member_idandloans.copy_idare two foreign keys in the same table: a loan connects one member with one copy.
Vocabulary: the table containing the foreign key is the child or referencing table; the one containing the primary key being pointed at is the parent or referenced table.
Two important observations:
- A foreign key can accept
NULL(unless it is declaredNOT NULL), and thatNULLmeans "this row points at nobody". If we allowedcopies.branch_idto be null, we would be saying there are copies with no branch assigned. In BiblioRed we do not want that, so it will beNOT NULL. - A foreign key can repeat.
branch_idbeing 2 in hundreds of members is exactly what we expect: it is a one-to-many relationship.
The practical handling of foreign keys —what happens when you delete the parent row, ON DELETE CASCADE, RESTRICT, deferrable constraints— is the entire content of lesson 02-06. Here we stay with the concept.
- Natural key versus surrogate key: the ISBN case
This is a real design debate, and BiblioRed has it right in front of it.
- A natural key is an identifier that already exists in the real world and that we have adopted as a key: a book's ISBN, a person's tax ID, an airport's IATA code.
- A surrogate key (or artificial key) is a number the database itself invents, with no meaning outside it:
book_id = 331.
Should books have the isbn (natural) as its primary key, or a book_id (surrogate)? Let's compare:
| Criterion | Natural key (isbn) |
Surrogate key (book_id) |
|---|---|---|
| Meaning | Makes sense outside the DB | None; useful only inside |
| Stability | Can change or be corrected (ISBN-10 → ISBN-13, cataloging typos) | Never changes |
| Size | 13 characters, replicated in every foreign key | 4 bytes of integer |
| Readability when debugging | High: you see the ISBN and know which book it is | Low: 331 says nothing |
| Universality | Not every item has one: magazines, pamphlets, old donations | Always exists |
| Risk of duplicates | Real: badly cataloged reprints share an ISBN by mistake | None |
Decision for BiblioRed: surrogate primary key book_id, and isbn as a UNIQUE alternate key. The three reasons that weigh most:
- Not everything BiblioRed lends has an ISBN. Holdings from before 1970, municipal publications and uncataloged donations would be left with no key. And a primary key cannot accept
NULL(we will see this in the next section). - ISBNs get corrected. When a librarian spots that an ISBN was mistyped and fixes it, with a natural key the change would have to be propagated to every child table; with a surrogate key, a single value is corrected and nobody else notices.
- Foreign keys get cheaper.
copieshas 40,000 rows: storing a 4-byte integer in each one instead of a 13-character string reduces the size of the table and of its indexes.
What we do not do is give up on the ISBN: it stays declared UNIQUE, so it remains impossible to catalog the same book twice. Choosing a surrogate key is no excuse for losing real-world constraints; that is the classic mistake.
The same reasoning applies to copies: the physical label EJ-3081 stuck on the copy is an excellent natural key for the front desk, but we will store it in a code column with UNIQUE, and the primary key will be the integer copy_id.
- The model's three integrity rules
The relational model defines three rules that every system must enforce. They are called integrity rules because their job is to stop the database from entering an impossible state.
Domain integrity
Every value of an attribute must belong to its domain.
It is the most elementary rule and the one that saves the most work. If the domain of loan_date is dates, the manager rejects 'yesterday', '32/13/2026' and '-1'. If the domain of publication_year is integers, it rejects 'nineteen eighty-nine'.
In SQL, domain integrity is expressed with data types (we will see them in action in 02-02) and refined with CHECK and NOT NULL constraints (full catalog in 04-04).
BiblioRed's spreadsheet had no domain integrity: that is why 2026-06-02, 02/06/26 and pending coexisted in the same date column.
Entity integrity
No attribute of the primary key can be NULL.
The reasoning is direct: the primary key exists to identify the tuple. If its value is unknown, the tuple is not identifiable, and then it cannot be part of a relation (remember: no duplicates, and two tuples with an unknown key cannot be told apart).
From this follows the practical consequence of the previous section: since not every BiblioRed item has an ISBN, the ISBN cannot be the primary key, because it would have to accept NULL.
In SQL this rule is automatic: declaring PRIMARY KEY implies NOT NULL even if you do not write it.
Referential integrity
Every non-null value of a foreign key must correspond to an existing value of the referenced primary key.
Stated that simply, it forbids orphan rows: a loan whose member_id is 9999 when member 9999 does not exist; a copy of a book that is not in the catalog. BiblioRed's spreadsheet had them by the dozen, because nothing stopped anyone from typing a made-up member number.
How it is declared, what the manager checks on each operation and what to do when you delete the parent row is the entire content of lesson 02-06. Here it is enough to retain the statement.
- The
NULL value and three-valued logic
NULL value and three-valued logicNULL is not zero. NULL is not the empty string. NULL is not "false". NULL is the absence of a value, and it admits at least three different readings that the model does not distinguish:
- Unknown: member 19, Pau Miralles, has an email address but did not give it when signing up.
- Not applicable: the
return_dateof a loan that is still open — the book has not been returned, so there is no date to put. - Pending: it has not been recorded yet.
In BiblioRed we will use return_date IS NULL as the operational definition of "open loan". It is a clean and very common use: the absence of a date means something.
Three-valued logic
Since NULL means "I don't know", any comparison with NULL produces… I don't know. SQL formalizes this with a three-valued logic: TRUE, FALSE and UNKNOWN.
| Expression | Result |
|---|---|
5 = 5 |
TRUE |
5 = 3 |
FALSE |
5 = NULL |
UNKNOWN |
NULL = NULL |
UNKNOWN |
NULL <> NULL |
UNKNOWN |
Yes: NULL = NULL is not true. And it makes perfect sense: if I know neither Marta's age nor Iván's, I cannot claim they are equal.
Truth tables for the logical operators (U = unknown):
A |
B |
A AND B |
A OR B |
|---|---|---|---|
| T | T | T | T |
| T | F | F | T |
| T | U | U | T |
| F | F | F | F |
| F | U | F | U |
| U | U | U | U |
Two cells deserve attention: FALSE AND UNKNOWN is FALSE (if one part already fails, the rest does not matter) and TRUE OR UNKNOWN is TRUE (if one part is already satisfied, the rest does not matter).
The consequence that causes the most grief
A filter only lets through the rows for which the condition is TRUE. UNKNOWN does not pass. That is why:
-- WRONG: returns NOTHING, not even the open loans
SELECT * FROM loans WHERE return_date = NULL;
-- RIGHT: the correct operator for asking about absence
SELECT * FROM loans WHERE return_date IS NULL;The first query does not raise a syntax error —and that is what makes it dangerous—, it simply returns zero rows every time. The correct operators are IS NULL and IS NOT NULL. We will practice them in 02-03.
A subtler and very real trap: WHERE status <> 'on_loan' does not return the rows whose status is NULL, because NULL <> 'on_loan' is UNKNOWN. If you want to include them, you have to ask: WHERE status <> 'on_loan' OR status IS NULL.
- Introduction to relational algebra
Codd did not stop at defining what a relation is: he also defined an algebra for operating on them. The idea is elegant: each operation takes one or two relations and returns another relation. Being closed (relation in, relation out), operations can be chained indefinitely, and from that the possibility of querying is born.
Here we present it conceptually only, with no notation exercises. What matters is that you see how each algebra operation has its direct translation into SQL: SQL is the practical realization of relational algebra, and the optimizer we saw in 01-04 works precisely by reordering these operations so that they cost less.
Selection (σ)
Picks rows that satisfy a condition. The result has the same degree and equal or lower cardinality.
"The copies at branch 2" → σ branch_id = 2 (copies)
Projection (π)
Picks columns. The result has lower degree. In pure algebra, projection removes duplicates (because the result must be a set); in SQL it does not, unless you write DISTINCT.
"Only the title and the ISBN of the books" → π title, isbn (books)
Cartesian product (×)
Combines each tuple of one relation with each tuple of the other. If members has 12,000 rows and books 8,000, the product has 96,000,000. It is rarely wanted for its own sake, but it is the theoretical foundation of every combination.
Join (⋈)
A Cartesian product followed by a selection that pairs up the related tuples. It is the operation that reassembles information spread across several tables.
"Each loan with its member's details" → loans ⋈ loans.member_id = members.member_id members
It is so central that we devote the whole of lesson 02-04 to it.
Union (∪)
Puts together the tuples of two compatible relations (same number of attributes and compatible domains) and removes duplicates.
"All the member identifiers that appear in loans or in reservations"
Difference (−)
The tuples that are in the first relation and not in the second. It is the operation that answers negative questions.
"Members who have made reservations but have never borrowed anything"
A map of correspondences
| Algebra operation | Symbol | SQL clause |
|---|---|---|
| Selection | σ | WHERE |
| Projection | π | the SELECT column list (+ DISTINCT) |
| Cartesian product | × | CROSS JOIN |
| Join | ⋈ | JOIN ... ON |
| Union | ∪ | UNION |
| Difference | − | EXCEPT (MINUS in Oracle) |
| Intersection | ∩ | INTERSECT |
| Rename | ρ | AS |
This table is, in practice, the index of the next three lessons.
- The conceptual schema of BiblioRed
With all the vocabulary in hand, this is the blueprint we will build in lesson 02-02. Seven relations:
erDiagram
BRANCHES ||--o{ MEMBERS : "is home branch of"
BRANCHES ||--o{ COPIES : "holds"
AUTHORS ||--o{ BOOKS : "writes"
BOOKS ||--o{ COPIES : "has copies in"
MEMBERS ||--o{ LOANS : "makes"
COPIES ||--o{ LOANS : "is the object of"
MEMBERS ||--o{ RESERVATIONS : "requests"
BOOKS ||--o{ RESERVATIONS : "is the object of"
BRANCHES {
int branch_id PK
varchar name UK
varchar address
varchar phone
date opening_date
}
MEMBERS {
int member_id PK
varchar first_name
varchar last_name
varchar email UK
date join_date
int branch_id FK
boolean active
}
AUTHORS {
int author_id PK
varchar first_name
varchar last_name
varchar nationality
int birth_year
}
BOOKS {
int book_id PK
varchar isbn UK
varchar title
int author_id FK
varchar publisher
int publication_year
varchar language
}
COPIES {
int copy_id PK
varchar code UK
int book_id FK
int branch_id FK
varchar status
date acquisition_date
}
LOANS {
int loan_id PK
int member_id FK
int copy_id FK
date loan_date
date due_date
date return_date
numeric surcharge
}
RESERVATIONS {
int reservation_id PK
int member_id FK
int book_id FK
date reservation_date
date expiry_date
varchar status
}
The design decisions we can already justify with what we have learned:
- Seven surrogate primary keys, one per table, all integers and auto-generated. They satisfy entity integrity without depending on real-world data.
books.isbnandcopies.codeas alternate keys (UNIQUE): we keep the natural constraints without turning them into the primary key.- The distinction between
booksandcopiesis the heart of the model.booksis the work (the title, the ISBN, the author);copiesis the physical object that gets lent and that sits on a specific shelf. BiblioRed has about 8,000 distinct books and 40,000 copies. Lending a "book" means nothing: what gets lent is a copy. loanspoints atcopies, not atbooks, precisely because of the above.reservations, by contrast, points atbooks: a member reserves the work, and whichever copy frees up first will be assigned to them. This asymmetry is not an oversight, it is the business model.return_dateacceptsNULLand thatNULLmeans "open loan". It is the legitimate use of the null value we saw in section 9.surchargeisNUMERIC, not floating point, because it represents money. The full justification is in the next lesson.
This schema is not yet a finished design: a book can have several authors, and books.author_id only has room for one. It is a conscious simplification for module 2; the techniques for modeling that case properly (E-R diagrams, N:M relationships, junction tables) arrive in module 4, and the theory that explains why certain designs degenerate, in module 5.
Common Mistakes and Tips
- Confusing "relation" with "link between tables". A relation is a table. The link is a foreign key. It is misunderstanding number one of relational vocabulary.
- Assuming rows have an order. "The most recent loans are at the end of the table" is false. Without
ORDER BYthere is no guaranteed order, and trusting whatever comes out today is a deferred breakdown. - Designing tables without a primary key. SQL allows it, which is why you have to impose it on yourself. Without a primary key you can end up with two identical rows that cannot be updated or deleted separately.
- Writing
= NULLinstead ofIS NULL. It raises no error: it silently returns zero rows. It is the most expensive mistake in this lesson. - Choosing a natural key out of convenience. The ISBN looks perfect until the first municipal pamphlet with no ISBN turns up, or the first typo that has to be corrected in cascade.
- Dropping the
UNIQUEbecause there is already a surrogate key. Addingbook_iddoes not authorize you to allow two books with the same ISBN. The surrogate key identifies; the alternate key protects reality. - Using
NULLas an all-purpose wildcard. IfNULLinstatussometimes means "I don't know" and sometimes "deregistered", nobody will ever query that column with confidence again. OneNULL, one meaning. - Tip: when a future query returns fewer rows than expected, check first whether
NULLs are involved. Three-valued logic is behind most "inexplicable" results.
Exercises
Exercise 1: Formal vocabulary over a relation
Given this instance of BiblioRed's copies relation:
| copy_id | code | book_id | branch_id | status | acquisition_date |
|---|---|---|---|---|---|
| 1 | EJ-3081 | 331 | 2 | on_loan | 2019-03-14 |
| 2 | EJ-3082 | 331 | 1 | available | 2019-03-14 |
| 3 | EJ-3083 | 331 | 3 | available | 2021-06-01 |
| 4 | EJ-3084 | 332 | 1 | on_loan | 2015-11-20 |
Answer:
- What is the degree and what is the cardinality?
- Propose a reasonable domain for
statusand another foracquisition_date. - State two superkeys, all the candidate keys, the chosen primary key and the alternate key.
- Is
{book_id, branch_id}a candidate key? Justify your answer with the data. - List this relation's foreign keys and where they point.
Exercise 2: Three-valued logic
The loans table contains these rows:
| loan_id | member_id | return_date | surcharge |
|---|---|---|---|
| 1 | 14 | 2026-03-19 | 0.00 |
| 2 | 15 | 2026-04-02 | 1.40 |
| 9 | 14 | NULL | NULL |
| 12 | 19 | NULL | NULL |
Say how many rows each query returns and why:
SELECT * FROM loans WHERE return_date = NULL;SELECT * FROM loans WHERE return_date IS NULL;SELECT * FROM loans WHERE surcharge > 0;SELECT * FROM loans WHERE surcharge > 0 OR return_date IS NULL;SELECT * FROM loans WHERE member_id = 14 AND surcharge > 0;SELECT * FROM loans WHERE NOT (surcharge > 0);
Exercise 3: Translating questions into relational algebra
Express each question using the algebra operations (σ, π, ⋈, ∪, −) and say which SQL clause will correspond to it. Correct SQL syntax is not required yet.
- The codes of the copies that are under repair.
- The titles of all the books, without repeats.
- Each copy together with the title of its book.
- The members who have loans or reservations.
- The books that have never been reserved.
Solutions
Solution 1
- Degree 6 (six attributes:
copy_id,code,book_id,branch_id,status,acquisition_date). Cardinality 4 (four tuples). Degree belongs to the schema; cardinality, to the instance. - For
status, a closed domain of labels:{'available', 'on_loan', 'in_repair', 'withdrawn'}. Foracquisition_date, valid calendar dates, not later than today (a library does not acquire in the future). The first will be implemented with aCHECK, the subject of 04-04. - Superkeys:
{copy_id},{code},{copy_id, status},{code, book_id, branch_id}, the set of all attributes… Candidate keys:{copy_id}and{code}, because both identify and neither can be reduced. Primary key:copy_id(surrogate, stable, cheap in the foreign keys ofloans). Alternate key:code, declaredUNIQUE, because the physical label must also be unique. - No. With this data no combination repeats, but that is an accident of the instance: nothing stops branch 2 from having two copies of book 331 (in fact that is normal in a library). Keys are determined by the business rules, never by inspecting a specific instance; an instance can only refute a candidate key, never confirm it.
book_id→books.book_id, andbranch_id→branches.branch_id. Both should beNOT NULL: a copy with no book makes no sense and a copy with no branch cannot be located on the shelf.
Solution 2
| # | Rows | Reason |
|---|---|---|
| 1 | 0 | return_date = NULL yields UNKNOWN in all four rows (including where the value is NULL). The filter only lets TRUE through. It is the classic trap: it does not fail, it goes quiet. |
| 2 | 2 (loans 9 and 12) | IS NULL is the correct operator; it returns TRUE exactly where the value is missing. |
| 3 | 1 (loan 2) | For row 1, 0.00 > 0 is FALSE. For 9 and 12, NULL > 0 is UNKNOWN, and UNKNOWN does not pass the filter. |
| 4 | 3 (loans 2, 9 and 12) | Row 2: TRUE OR FALSE = TRUE. Rows 9 and 12: UNKNOWN OR TRUE = TRUE (it is enough for one part to hold). Row 1: FALSE OR FALSE = FALSE. |
| 5 | 0 | Member 14's rows are number 1 (0.00 > 0 is FALSE → T AND F = FALSE) and number 9 (NULL > 0 is UNKNOWN → T AND U = UNKNOWN, does not pass). |
| 6 | 1 (loan 1) | NOT FALSE = TRUE (row 1). NOT TRUE = FALSE (row 2). NOT UNKNOWN = UNKNOWN (rows 9 and 12): negating something unknown is still unknown. This is the most counter-intuitive point: query 3 returns 1 row and its negation also returns 1, not 3. |
Solution 3
| # | Relational algebra | SQL clause |
|---|---|---|
| 1 | π code ( σ status = 'in_repair' (copies) ) |
SELECT code ... WHERE status = 'in_repair' |
| 2 | π title (books) — algebra's projection already removes duplicates |
SELECT DISTINCT title FROM books |
| 3 | copies ⋈ copies.book_id = books.book_id books |
JOIN ... ON (lesson 02-04) |
| 4 | π member_id (loans) ∪ π member_id (reservations) |
UNION |
| 5 | π book_id (books) − π book_id (reservations) |
EXCEPT (or a LEFT JOIN ... IS NULL, lesson 02-04) |
Notice the pattern in point 5: every question that starts with "the ones that never…" is a difference. Remember it, because in 02-04 and 02-06 it will come back several times.
Conclusion
We have turned the informal vocabulary of the first lesson into a model with precise rules:
- A relation is a set of tuples with the same attributes, each with its domain. Its degree is the number of attributes and its cardinality, the number of tuples.
- Being a set, it has no order and no duplicates, and that separates it radically from a spreadsheet, where a row is identified by its position and not by its value.
- Keys form a hierarchy: superkey → candidate key (minimal superkey) → primary key (the chosen one) and alternate keys (the rest,
UNIQUE). Foreign keys link relations to one another. - In the natural versus surrogate key debate, BiblioRed chooses
book_idas the primary key and keepsisbnas an alternate one: because not everything has an ISBN, because ISBNs get corrected and because an integer is cheaper to replicate. - The three integrity rules: domain (values belong to their type), entity (the primary key is never null) and referential (every foreign key points at something that exists; its practical handling is lesson 02-06).
NULLmeans "I don't know", and hence three-valued logic:NULL = NULLis UNKNOWN,IS NULLis the only valid operator for asking about its absence, andNOT UNKNOWNis still UNKNOWN.- Relational algebra —selection, projection, Cartesian product, join, union, difference— is the machinery SQL implements: each operation has its clause, and the optimizer from 01-04 does nothing but reorder them.
- And we have the BiblioRed blueprint:
branches,members,authors,books,copies,loansandreservations, with their keys and their links.
The blueprint is drawn; now it has to be built. In lesson 02-02, The SQL Language, you will meet the language used to talk to a relational system —why it is declarative, what sublanguages it has, how its data types are written— and you will finish by running the complete CREATE TABLE script for the seven tables inside biblioredb. When you finish it, this lesson's diagram will have stopped being a drawing.
Database Fundamentals
Module 1: Introduction to Databases
- Basic Database Concepts
- Types of Databases
- History and Evolution of Databases
- Database Management Systems and Architecture
Module 2: Relational Databases
- The Relational Model
- The SQL Language
- Basic SQL Operations
- Multi-Table Queries: JOINs and Subqueries
- Data Aggregation and Grouping
- Referential Integrity
Module 3: Non-Relational Databases
- Introduction to NoSQL
- Types of NoSQL Databases
- Data Modeling in NoSQL
- Comparing Relational and Non-Relational Databases
Module 4: Schema Design
- Schema Design Principles
- Entity-Relationship (ER) Diagrams
- Transforming ER Diagrams into Relational Schemas
- Data Types and Constraints
Module 5: Normalization
Module 6: Transactions, Performance, and Security
- Transactions and ACID Properties
- Concurrency and Isolation Levels
- Indexes and Query Optimization
- Security, Permissions, and Backups
Module 7: Practical Exercises
- SQL Exercises
- Schema Design Exercises
- Normalization Exercises
- Advanced Query and Transaction Exercises
Module 8: Case Studies
- Case Study: Relational Database
- Case Study: Non-Relational Database
- Case Study: Polyglot Persistence
