The Relational Model

Imagine a university registrar tracking students, courses, and enrollments using nothing but paper index cards. Every clerk files cards differently, cross-referencing them by hand. A single typo silently breaks the link between a student and a class. The registrar's real problem is not storage. It is finding a shared, unambiguous way to represent facts. This way must let anyone, or any program, query them correctly. This is precisely the problem the relational model was built to solve. The relational model, introduced by Edgar F. Codd in 1970, represents all data — no exceptions — as relations: mathematical tables of rows and columns. There are no pointers to follow, no nested records to traverse. There is no physical file layout to memorize. A user states what data they want using a small, precise mathematical language. The database system then figures out how to retrieve it. This separation between logical structure and physical storage is the single most consequential idea in the history of database systems. Why does this matter in real systems? Every relational database — PostgreSQL, MySQL, Oracle, SQL Server, SQLite — is, at its logical core, an implementation of this one model. Application programmers writing `SELECT FROM enrollments WHERE grade = 'A'` are working entirely within the abstraction Codd defined over fifty years ago. They remain oblivious to whether the underlying engine stores rows on disk pages, in columnar files, or in memory. That programmer-level independence from storage details is what the relational model guarantees. This article builds the relational model from first principles: what a relation actually is* as a mathematical object, how it differs from an ordinary spreadsheet or a programming-language array of records, and why its rigor pays off in correctness and query power. It sets up the vocabulary — tuples, attributes, domains, schemas, instances — needed for the deeper topics that follow. These include Relations, Attributes, and Keys and The Relational Algebra.

Key takeaways
  • A relation is a mathematical set of tuples over a fixed collection of named, typed attributes — this is the entire foundation of relational databases.
  • The relation schema (structure: names and domains) is distinct from the relation instance (the actual data at a moment in time).
  • Attribute values must be atomic (drawn from a domain) — no repeating groups or nested tables inside a cell, unlike arrays or JSON documents.
  • Because a relation is a set, tuples have no inherent order and there are no duplicate rows, unlike a spreadsheet or a CSV file.
  • Physical data independence means applications query relations without knowing how rows are stored, indexed, or ordered on disk.
  • The model's mathematical footing enables a precise query language (relational algebra) with provable equivalences used for optimization.
The core insight

Data is a set of tuples over named, atomic-valued attributes — a mathematical object, not a file format — and this abstraction is what lets query languages be both precise and optimizable.

The idea

A relation is defined over a set of attributes A1,A2,…,AnA_1, A_2, \ldots, A_n, each attribute AiA_i having an associated domain DiD_i — the set of legal atomic values that attribute may take (e.g., integers, strings of length ≤ 20, dates). Formally, a relation rr is a subset of the Cartesian product of the domains:

r⊆D1×D2×⋯×Dnr \subseteq D_1 \times D_2 \times \cdots \times D_n

Each element of this subset is a tuple t=(v1,v2,…,vn)t = (v_1, v_2, \ldots, v_n), where vi∈Div_i \in D_i is the value of attribute AiA_i in that tuple. Because rr is a set in the mathematical sense, two important consequences follow immediately: tuples are unordered (there is no "first row"), and there are no duplicate tuples (a set cannot contain the same element twice). This is a sharp, deliberate departure from a spreadsheet or a programming array, where row order and duplication are both meaningful. It is essential to separate two related but distinct notions. The relation schema specifies the structure: the relation's name, its attributes, and each attribute's domain — for example, `Student(ID: integer, name: varchar(30), dept: varchar(20), tot_cred: integer)`. The relation instance is the actual content at a given point in time — the current set of tuples satisfying that schema. A schema is defined once (via `CREATE TABLE` in SQL) and remains largely stable, while the instance changes constantly as rows are inserted, updated, and deleted. Confusing schema with instance is a common early mistake: the schema is the type, the instance is a value of that type. Each attribute must be atomic, meaning the domain contains indivisible values — a single integer, a single string — not sets, lists, or nested tuples. This atomicity requirement (part of what is informally called first normal form) is what makes relations simple to reason about mathematically and simple for a query language to manipulate uniformly: every operation on a relation produces another relation with the same kind of structure, which is what allows relational operators to be composed freely. A collection of relation schemas defines a relational database schema; the corresponding collection of instances, at some moment, is a relational database instance — the full state of the database.

How it works

The relational model is not a single algorithm but a representational discipline: any real-world entity or relationship is modeled as one or more relations, and every operation on data is expressed as an operation that maps relations to relations. The "mechanism" is best understood as the discipline of building and validating a schema, and the discipline of interpreting an instance against it.

Step 1 — Identify entities and relationships. Decide what real-world things need tracking (students, courses) and what facts connect them (enrollment, grade). Each becomes a candidate relation.

Step 2 — Define the schema. For each relation, list its attributes and their domains. Choose a candidate key — a minimal set of attributes whose values uniquely identify each tuple — and designate one as the primary key. (This is developed fully in Relations, Attributes, and Keys.)

Step 3 — Populate the instance. Insert tuples. The database system checks, on every insertion or update, that:

  • every attribute value lies in its declared domain (domain constraint),
  • no two tuples agree on the primary key (key constraint / entity integrity),
  • foreign key references point to existing tuples in the referenced relation (referential integrity).

Step 4 — Query. Users express requests as expressions over relations (relational algebra or SQL), which the database evaluates by producing new relations as results — never by exposing internal storage. The pseudocode below formalizes the validation logic a database engine conceptually performs whenever the instance changes — this is the invariant-preservation machinery that keeps an instance a faithful, consistent model of the schema. ``` // Validate a proposed tuple insertion against a relation schema R function VALIDATE_INSERT(R, t): // R.attributes = [A_1, ..., A_n] with domains D_1, ..., D_n // R.instance = current set of tuples // R.primary_key = subset of attribute indices // R.foreign_keys = list of (attrs, referenced_relation, referenced_key) // 1. Domain check: every value must belong to its attribute's domain for i in 1..n: if t[i] not in R.attributes[i].domain: reject "domain violation on attribute " + R.attributes[i].name // 2. Entity integrity: primary key attributes cannot be null, // and must be unique across the current instance for k in R.primary_key: if t[k] is NULL: reject "primary key attribute cannot be null" for existing_t in R.instance: if projects_equal(existing_t, t, R.primary_key): reject "duplicate primary key value" // 3. Referential integrity: every foreign key value must // match an existing tuple's key in the referenced relation for (attrs, ref_relation, ref_key) in R.foreign_keys: fk_value = project(t, attrs) if fk_value is not NULL and fk_value not in { project(s, ref_key) for s in ref_relation.instance }: reject "referential integrity violation" // All checks passed: t may be safely added R.instance = R.instance ∪ { t } // set union — duplicates collapse automatically return "insert accepted" ``` The single line `R.instance = R.instance ∪ { t }` is where the set semantics of the model becomes operationally real: even without an explicit uniqueness check on the whole tuple, set union guarantees that inserting an already-present tuple has no effect, which is exactly the "no duplicate rows" property the mathematical definition promises.

Figure
Schema vs. instance: the Student relation
Relation Schema: Student(ID, name, dept, tot_cred) Attributes and domains: ID: integer(9) • name: varchar(30) • dept: varchar(20) • tot_cred: integer ≥ 0 Relation Instance (a set of tuples, no order, no duplicates) ID name dept tot_cred 00128 Zhang Comp. Sci. 102 12345 Shankar Comp. Sci. 32 19991 Brandt History 80 Schema is fixed structure; instance changes on every insert / update / delete. Each tuple is a point in ID × name × dept × tot_cred — atomic values only, no nested rows.

Complexity & correctness

Space. A relation instance with rr tuples over nn attributes requires O(r⋅n)O(r \cdot n) space in the abstract model (before considering indexes, which trade extra space for faster lookups). This is a direct consequence of the definition: a tuple is a fixed-length vector of nn values, and the relation is simply rr such vectors.

Time for validation. The `VALIDATE_INSERT` procedure above costs, in the naive worst case:

  • O(n)O(n) for domain checks (constant per attribute, nn attributes),
  • O(r)O(r) for the primary-key uniqueness scan, since it may compare against every existing tuple,
  • O(m⋅rˉ)O(m \cdot \bar{r}) for referential integrity, where mm is the number of foreign keys and rˉ\bar{r} is the size of each referenced relation, again in the naive linear-scan case.

So a single insertion costs O(n+r+mrˉ)O(n + r + m\bar{r}) in the worst case with no auxiliary structures — this is honestly poor for large rr. Real systems avoid this by maintaining a B-tree or hash index on primary and foreign key attributes, reducing the uniqueness and referential checks to O(log⁡r)O(\log r) or expected O(1)O(1) respectively. This is precisely why physical data independence matters: the logical guarantee (uniqueness, referential integrity) is fixed by the model, while the physical mechanism enforcing it (linear scan vs. index lookup) is free to change without altering correctness or the schema visible to applications.

Correctness argument. The relational model's correctness rests on an invariant maintained across every state transition (insert, delete, update): > Invariant: At every point in time, the database instance satisfies all declared domain constraints, key constraints, and referential integrity constraints of its schema. The base case holds trivially for an empty database (vacuously satisfies all constraints). The inductive step is exactly what `VALIDATE_INSERT` (and its analogues for delete/update) enforces: a proposed transition is applied only if it preserves the invariant, and rejected otherwise. Because every mutation goes through such a check, and because the invariant holds initially, it holds after every subsequent operation by induction on the sequence of operations. This is the same style of reasoning used to prove loop invariants correct, applied here to a persistent data store rather than a single computation.

Tradeoffs. The rigor of the model — atomic values, set semantics, explicit constraints — is exactly what enables a small, closed query algebra with provable rewrite rules (used heavily by query optimizers). The cost is expressive friction: representing naturally hierarchical or repeating data (e.g., a student's list of phone numbers) requires decomposing it into separate relations rather than nesting it directly, which is why document/NoSQL models exist as an alternative for workloads dominated by nested, schema-flexible data.

Worked example

Consider a small university database with two relations. ``` Student(ID, name, dept, tot_cred) Course(course_id, title, dept, credits) ``` Suppose the current instance of `Student` is:

IDnamedepttot_cred
00128ZhangComp. Sci.102
12345ShankarComp. Sci.32
19991BrandtHistory80

Now trace three attempted operations through the validation logic.

Operation 1 — Insert (76543, "Levy", "Physics", 46).

  • Domain check: `ID=76543` is an integer within range, `name` is a valid varchar, `dept="Physics"` valid, `tot_cred=46 ≥ 0`. Pass.
  • Entity integrity: scan the 3 existing tuples for `ID=76543` — no match found. Pass.
  • Referential integrity: none declared on `Student` in this simplified schema. Pass.
  • Result: accepted. New instance has 4 tuples.

Operation 2 — Insert (12345, "Kim", "Physics", 15).

  • Domain check passes (all values valid).
  • Entity integrity: scan existing tuples — `ID=12345` already exists (Shankar's row).
  • Result: rejected — duplicate primary key value. This is the entity-integrity rule preventing two distinct real-world students from colliding under one identifier.

Operation 3 — Insert (55555, "Patel", "Comp. Sci.", -10).

  • Domain check: `tot_cred = -10` violates the declared domain `integer ≥ 0`.
  • Result: rejected — domain violation on attribute tot_cred. Note this check fires before the key or referential checks even run, matching the order in the pseudocode — cheaper checks are evaluated first, a small but real optimization.

This trace demonstrates all three integrity mechanisms firing on realistic, non-symmetric inputs: one clean success, one key collision, and one domain violation — the three distinct failure modes a well-designed relational schema must guard against.

Common pitfalls & traps

PitfallWhy it happensConsequence
Treating a relation as an ordered listConfusing SQL's `SELECT` output (which can be ordered by `ORDER BY`) with the underlying relation, which has no inherent orderQueries that silently rely on row order are non-portable and can break when the optimizer changes execution plans
Allowing non-atomic attribute values (e.g., a comma-separated list in one cell)Modeling a one-to-many fact directly inside a single tuple for convenienceViolates first normal form; makes filtering, indexing, and joining on that "hidden" sub-list impossible without string parsing
Confusing schema with instanceUsing "the database" to mean both structure and current data interchangeablyLeads to unclear requirements: "does this constraint apply to the type or to this snapshot?"
Assuming duplicate tuples are automatically eliminated everywhere in SQLSQL tables, unlike pure relations, permit duplicate rows unless a key or `DISTINCT` is usedQuery results with unexpected duplicate rows when set semantics were assumed but not enforced
Skipping referential integrity checks for performanceBelieving application-level checks are "good enough"Orphaned foreign keys (e.g., an enrollment referencing a deleted course) silently corrupt derived queries and reports

Pioneers & history

Origins · 1 minEdgar Frank Codd and how this was discovered
EF
Edgar Frank Codd
1923–2003 · IBM Research, San Jose — creator of the relational model

Trained originally as a mathematician at Oxford, Codd joined IBM in 1949 and spent the 1960s frustrated by the rigidity of the hierarchical and network database systems of the era, which forced application programmers to navigate explicit pointer structures. In 1970 he published "A Relational Model of Data for Large Shared Data Banks," arguing that data should be represented purely as mathematical relations, freeing programs from dependence on physical storage layout. IBM was slow to act on his own paper commercially, but the idea directly inspired System R at IBM and, independently, Ingres at Berkeley — the two research prototypes from which nearly every modern relational database descends. Codd received the Turing Award in 1981 for this work.

History
1970
— Codd publishes the relational model in Communications of the ACM.
1974
— IBM's System R project begins, developing SQL (originally SEQUEL) as a query language for relations.
1979
— Oracle ships the first commercially available SQL-based relational database.
1985
— Codd publishes his twelve rules for what qualifies as "truly relational" database systems.
1986
— SQL is standardized by ANSI, cementing the relational model as the industry default.

Where this matters

Every major relational database management system — PostgreSQL, MySQL, Oracle Database, Microsoft SQL Server, SQLite, IBM Db2 — implements the relational model as its logical data model, with SQL as the standard query interface built on top of relational algebra. Financial systems, airline reservation systems, hospital records, and e-commerce inventories all rely on the model's integrity guarantees (entity and referential integrity) to keep critical facts consistent even under heavy concurrent write load. Data warehousing and business intelligence tools issue analytical queries expressed as relational operations, trusting the query optimizer to exploit the algebra's mathematical equivalences to choose an efficient execution plan without the analyst ever specifying how to compute the answer. Even systems that reject strict relational structure — document stores, key-value stores, graph databases — are typically explained and compared against the relational baseline, since it remains the reference point for what a "table," a "key," and a "join" mean across the entire database field. The concepts introduced here — schema versus instance, atomic domains, set-based tuples — are the direct foundation for Relations, Attributes, and Keys (how uniqueness and constraints are formalized) and for The Relational Algebra (how queries are expressed and combined as operations on these very relations).

Ask the tutor: How does the relational model's mathematical set-based definition constrain and enable the design of the relational algebra's operators?

Is a relational database table exactly the same thing as a mathematical relation?

Not quite. A mathematical relation is strictly a set of tuples: no duplicates, no order. Real SQL tables are technically multisets (bags) unless a `PRIMARY KEY` or `UNIQUE` constraint forces set-like behavior, and query results without `DISTINCT` can legally contain duplicate rows. The relational model is the idealized mathematical foundation; SQL is a practical language built atop it that relaxes a few of these guarantees for performance and convenience (for example, allowing duplicate rows makes multiset aggregation operations like `SUM` and `COUNT` far cheaper to compute). Understanding the pure model first makes it much easier to see exactly where and why SQL's behavior deviates from strict set theory.

Why can't an attribute's value be a list or a nested table?

The relational model requires every attribute value to be atomic — drawn indivisibly from its domain — which is the condition informally called first normal form. If an attribute could hold a nested list (say, a student's multiple phone numbers packed into one cell), then operations like selection, projection, and join would need special-cased logic to look inside that cell, breaking the uniformity that lets every relational operation take relations as input and produce relations as output. The standard fix is decomposition: create a separate `Phone(student_id, number)` relation and connect it via a foreign key, preserving atomicity while still representing the one-to-many fact.

What is the difference between a relation schema and a relation instance, in practice?

The schema is the structural declaration — attribute names and domains — created once with a statement like `CREATE TABLE Student(ID INT, name VARCHAR(30), ...)`. It rarely changes and defines what a valid row can look like. The instance is the actual set of rows present in the table right now, which changes constantly through `INSERT`, `UPDATE`, and `DELETE`. A useful analogy: the schema is like a class definition in a programming language, while the instance is like the current set of live objects of that class at some moment during program execution.

Does the relational model require every relation to have a primary key?

Conceptually, yes — Codd's entity integrity rule states that every relation should have a primary key so that every tuple is uniquely identifiable, since a relation is defined as a set, and identifying its elements is fundamental to referencing them from elsewhere. In practice, some SQL tables are created without an explicit primary key, which SQL permits but which weakens the guarantees the model is designed to provide — duplicate rows become possible, and other tables cannot reliably reference specific rows via foreign keys. Declaring keys is considered essential relational database design discipline, covered in depth alongside Relations, Attributes, and Keys.

How does the relational model relate to SQL, and why isn't SQL identical to relational algebra?

SQL is the dominant practical query language implemented over relational databases, and its core operations (`SELECT`, `JOIN`, `WHERE`) correspond closely to operators in relational algebra (projection, join, selection) — this correspondence is developed fully in The Relational Algebra. However, SQL diverges from the pure model in several ways: it permits duplicate rows (bag semantics) by default, supports `NULL` values with three-valued logic not present in the classical model, and includes procedural extensions (stored procedures, triggers) that go beyond a purely declarative algebra. SQL is best understood as an engineering realization of relational theory, not a one-to-one translation of it.

What was so revolutionary about representing all data as tables?

Before 1970, database systems (hierarchical and network models) exposed physical pointer structures directly to programmers: querying data meant navigating explicit chains of records, and any change to physical storage layout could break every application built on top of it. Codd's insight was that a simple, uniform mathematical structure — the relation — could represent arbitrary data while completely hiding physical storage details, giving rise to physical data independence. This let query languages be declarative (state what you want) rather than navigational (state how to walk the pointers), and it let a query optimizer, not the programmer, decide the most efficient access path.

Can the relational model represent hierarchical or graph-shaped data, like an organizational chart or a social network?

Yes, but indirectly, through decomposition and foreign keys rather than direct nesting. A hierarchy (employee reporting to a manager) is modeled by adding a foreign key attribute, such as `manager_id`, referencing the same relation's primary key — a self-referencing relationship. A graph (friendships) is modeled with a separate relation holding pairs of connected IDs. Recursive or deeply nested queries over such structures (e.g., "all employees under a given manager, at any depth") require recursive query constructs layered on top of the base algebra, since the elementary relational operators alone are not recursive.

Before this

Where this leads