Skip to content

Relational Theory

E.F. Codd introduced the relational model in his 1970 paper “A Relational Model of Data for Large Shared Data Banks.” The model provides a mathematically rigorous foundation for data management Based on set theory and first-order predicate logic. Every SQL database is an approximation of this Model — and understanding where SQL deviates from the theory helps you write correct queries.

Codd defined 13 rules (numbered 0 through 12) that a system must satisfy to be considered truly Relational. No commercial database fully satisfies all 13, but they serve as the theoretical Benchmark:

RuleNameSummary
0FoundationA relational DBMS must manage databases through its relational capabilities alone
1InformationAll information is represented as values in tables
2Guaranteed AccessEvery value is accessible by table name, primary key, and column name
3Systematic Treatment of NULLNULL values are distinct from default values and represent missing information
4Dynamic Online CatalogThe database description (catalog) is represented as relational tables
5Comprehensive SublanguageSupports at least one relational language (SQL, QBE, etc.)
6View UpdatingAll views theoretically updatable must be updatable by the system
7High-level Insert/Update/DeleteSet-level operations, not row-by-row processing
8Physical Data IndependenceApplication logic unaffected by physical storage changes
9Logical Data IndependenceApplication logic unaffected by logical schema changes (view changes)
10Integrity IndependenceIntegrity constraints are part of the schema, not the application
11Distribution IndependenceApplications unaffected by data distribution
12NonsubversionLow-level language cannot bypass integrity constraints

In practice, Rule 6 (view updating) is the most commonly violated. Most SQL databases cannot update Through arbitrary views, especially those involving joins, aggregations, or DISTINCT.

Relations, Tuples, Attributes, and Domains

Section titled “Relations, Tuples, Attributes, and Domains”

The relational model uses precise terminology that SQL conflates:

Relational TermSQL TermMathematical Object
RelationTableA set of tuples (no duplicate tuples)
TupleRowAn ordered list of values
AttributeColumnA named position in a tuple
DomainData typeA set of permissible values
DegreeArityNumber of attributes in a relation
CardinalityRow countNumber of tuples in a relation

A mathematical relation is a set of tuples, which means:

  1. No duplicate tuples — SQL tables allow duplicates (unless you declare UNIQUE or PRIMARY KEY). To get true relational behavior, you must use SELECT DISTINCT.
  2. No ordering of tuples — SQL ORDER BY operates on the result set, not on the base relation. A table has no inherent row order.
  3. Attributes are identified by name, not position — SQL allows SELECT * which relies on column ordering. This is a deviation from the theory.

A domain defines the set of valid values for an attribute. SQL data types (INTEGER VARCHAR(255)``DATE) are a coarse approximation of domains. A true domain would include Constraints:

CREATE DOMAIN age_domain AS INTEGER
CHECK (VALUE >= 0 AND VALUE <= 150);
CREATE DOMAIN email_domain AS VARCHAR(255)
CHECK (VALUE ~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$');

PostgreSQL arrays (INTEGER[]) technically violate 1NF but are a pragmatic extension. When you need To query individual elements or enforce referential integrity on array elements, model them as Separate rows.

A relation is in 2NF if it is in 1NF and no non-prime attribute is partially dependent on any Candidate key. “Partially dependent” means dependent on a proper subset of a candidate key.

2NF only matters for relations with composite candidate keys. If all candidate keys are single Attributes, the relation is automatically in 2NF if it is in 1NF.

R(order_id, product_id, quantity, product_name)
FDs: {order_id, product_id} → quantity, product_id → product_name
product_name depends on product_id (a subset of the key), not the full key.
This violates 2NF.
Fix: split into:
OrderItem(order_id, product_id, quantity)
Product(product_id, product_name)

A relation is in 3NF if it is in 2NF and no non-prime attribute is transitively dependent on any Candidate key. Equivalently, for every non-trivial FD XAX \rightarrow A where AA is non-prime, XX Must be a superkey.

R(emp_id, name, dept_id, dept_name, dept_location)
FDs: emp_id → name, dept_id, dept_name, dept_location
dept_id → dept_name, dept_location
dept_name and dept_location transitively depend on emp_id via dept_id.
This violates 3NF.
Fix: split into:
Employee(emp_id, name, dept_id)
Department(dept_id, dept_name, dept_location)

A relation is in BCNF if for every non-trivial FD XYX \rightarrow Y, XX is a superkey. BCNF is Stricter than 3NF: 3NF allows XAX \rightarrow A where XX is a superkey OR AA is a prime attribute. BCNF removes the “or AA is prime” exception.

R(student, course, instructor)
FDs: {student, course} → instructor
instructor → course
Neither student nor instructor alone is a superkey.
The candidate keys are {student, course} and {student, instructor}.
instructor → course violates BCNF (instructor is not a superkey).
Fix: split into:
Teaching(instructor, course)
StudentInstructor(student, instructor)