Loading learning content...
By the mid-1980s, the term "relational database" had become a marketing buzzword. Vendors slapped the label on products that were only superficially relational—databases that might use tables for display but lacked the mathematical foundations that gave the relational model its power.
E.F. Codd, the inventor of the relational model, watched this corruption of his work with growing concern. In 1985, he published a remarkable article in Computerworld that would become one of the most influential pieces in database history: "Is Your DBMS Really Relational?"
In this article and its sequel "Does Your DBMS Run By the Rules?", Codd articulated twelve rules (actually thirteen, numbered 0-12) that a database management system must satisfy to be called fully relational. These rules weren't arbitrary requirements—they were precise translations of the relational model's mathematical foundations into practical implementation criteria.
More than requirements, Codd's rules became a benchmark for evaluating database systems and a roadmap for database development. Let's examine each rule in detail.
By the end of this page, you will understand each of Codd's 12 rules (plus Rule 0), the theoretical rationale behind each rule, how real database systems comply (or fail to comply) with these rules, and why these rules remain relevant benchmarks today.
For any system that is advertised as, or claimed to be, a relational database management system, that system must be able to manage databases entirely through its relational capabilities.
Interpretation
Rule 0 is the meta-rule that establishes the context for all other rules. It states that a system claiming to be relational must use relational features—not non-relational workarounds—to manage data.
This might seem obvious, but consider systems that:
If any essential database operation requires stepping outside the relational paradigm, the system isn't fully relational.
Why This Matters
Rule 0 ensures that the benefits of the relational model—data independence, declarative querying, optimization—apply to ALL database operations, not just simple ones. A system that's relational "except for" certain cases forces users back into the complexity the relational model was designed to eliminate.
System A:
- Data queries: SQL ✓
- Data modification: SQL ✓
- Schema changes: SQL ✓
- Backup/recovery: SQL commands ✓
- Permission management: SQL (GRANT/REVOKE) ✓
- Metadata access: SQL (information_schema) ✓System A Verdict: COMPLIANT with Rule 0
All database management uses relational capabilities.
System B:
- Data queries: SQL ✓
- Bulk loading: Proprietary binary format ✗
- Statistics: Must run external utility ✗
System B Verdict: PARTIAL compliance onlyAll information in a relational database is represented explicitly at the logical level and in exactly one way—by values in tables.
Interpretation
This is perhaps the most fundamental rule. It establishes that:
The Single Representation Principle
Pre-relational databases used different representations for different data:
The relational model unifies everything: tables all the way down.
Implications
123456789101112131415161718192021222324252627
-- The information rule in action: metadata as tables-- Query the system catalog just like any other table -- List all tables in PostgreSQLSELECT table_name, table_typeFROM information_schema.tablesWHERE table_schema = 'public'; -- List all columns with their typesSELECT table_name, column_name, data_type, is_nullableFROM information_schema.columnsWHERE table_schema = 'public'; -- List all foreign key relationshipsSELECT tc.table_name AS referencing_table, kcu.column_name AS referencing_column, ccu.table_name AS referenced_table, ccu.column_name AS referenced_columnFROM information_schema.table_constraints tcJOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_nameJOIN information_schema.constraint_column_usage ccu ON tc.constraint_name = ccu.constraint_nameWHERE tc.constraint_type = 'FOREIGN KEY'; -- All the above use standard SQL against standard tables!The system catalog (information_schema, pg_catalog, etc.) IS a relational database about your database. This self-describing property enables tools like schema browsers, ORM introspection, and automatic documentation generators—all using the same SQL skills you use for data.
Each and every datum (atomic value) in a relational database is guaranteed to be logically accessible by resorting to a combination of table name, primary key value, and column name.
Interpretation
This rule guarantees addressability. Every piece of data can be located precisely through three coordinates:
No Navigation Required
In hierarchical or network databases, accessing data might require:
Rule 2 eliminates this. You specify WHAT you want, not HOW to get there.
Primary Keys are Mandatory
Rule 2 implicitly requires that every table has a primary key. Without a reliable way to identify individual rows, you cannot guarantee access to specific values.
This is why database design always emphasizes key selection—it's not just good practice, it's fundamental to relational access.
To access Alice Chen's salary from the Employee table:
Coordinates:
- Table: Employee
- Primary Key: emp_id = 1001
- Column: salary
SQL Query:
SELECT salary FROM Employee WHERE emp_id = 1001;Result: 95000.00
This access is:
✓ Guaranteed to work (if the key exists)
✓ Independent of physical storage
✓ Same syntax regardless of table size
✓ No navigation path needed
Compare to hierarchical navigation:
FIND Department WHERE name = 'Engineering'
THEN FIND Employee UNDER Department WHERE emp_id = 1001
THEN GET salary
The relational approach is simpler AND more powerful.SQL allows tables without primary keys, which technically violates Rule 2. While the DBMS permits this, it creates ambiguity—if duplicate rows exist, you cannot uniquely identify values. Best practice (and Rule 2 compliance) requires defining a primary key for every table.
NULL values (distinct from the empty character string or a string of blank characters and distinct from zero or any other number) are supported in fully relational DBMS for representing missing information and inapplicable information in a systematic way, independent of data type.
Interpretation
Rule 3 addresses a critical real-world problem: sometimes data is unknown or doesn't apply. Codd recognized that a principled solution was essential.
NULL is NOT:
NULL IS:
Systematic Treatment
NULL must be handled consistently across all operations:
This consistent treatment is what "systematic" means—NULL behavior is defined, not arbitrary.
| Expression | Result | Explanation |
|---|---|---|
| NULL = NULL | UNKNOWN | We don't know if two unknowns are equal |
| NULL <> NULL | UNKNOWN | We don't know if two unknowns differ |
| 5 > NULL | UNKNOWN | We can't compare known to unknown |
| NULL AND TRUE | UNKNOWN | Unknown AND anything that isn't FALSE = Unknown |
| NULL AND FALSE | FALSE | Regardless of unknown, FALSE wins |
| NULL OR TRUE | TRUE | Regardless of unknown, TRUE wins |
| NULL OR FALSE | UNKNOWN | Result depends on the unknown value |
| NOT NULL | UNKNOWN | Negation of unknown is unknown |
123456789101112131415161718192021222324252627282930
-- Proper NULL handling in SQL -- Testing for NULL (= doesn't work!)SELECT * FROM Employee WHERE manager_id IS NULL; -- CorrectSELECT * FROM Employee WHERE manager_id = NULL; -- WRONG! Returns nothing -- Coalesce: provide default for NULLSELECT name, COALESCE(manager_id, 0) AS mgr FROM Employee; -- NULLIF: return NULL if values matchSELECT NULLIF(bonus, 0) AS actual_bonus -- Treat 0 bonus as "no bonus"FROM Employee; -- NULL in aggregatesSELECT COUNT(*) AS total_rows, -- Counts all rows including NULL COUNT(bonus) AS with_bonus, -- Counts non-NULL bonus values SUM(bonus) AS total_bonus, -- Sums non-NULL values AVG(bonus) AS avg_bonus -- Average of non-NULL values onlyFROM Employee; -- CASE with NULLSELECT name, CASE WHEN bonus IS NULL THEN 'No bonus assigned' WHEN bonus = 0 THEN 'Zero bonus' ELSE 'Has bonus: ' || bonus::text END AS bonus_statusFROM Employee;NULL introduces three-valued logic, which complicates reasoning. Many subtle bugs arise from unexpected NULL behavior: WHERE x <> 5 doesn't return rows where x IS NULL. Join conditions with NULL never match. Aggregate functions behave differently. Understanding NULL semantics is crucial for correct SQL.
The database description is represented at the logical level in the same way as ordinary data, so that authorized users can apply the same relational language to its interrogation as they apply to the regular data.
Interpretation
Rule 4 extends Rule 1 specifically for metadata. The database catalog (schema information) must be:
The System Catalog
Modern databases implement this through system catalogs:
pg_catalog schema, information_schema viewinformation_schema, mysql databasesys schema, INFORMATION_SCHEMA viewsDBA_*, ALL_*, USER_* views, INFORMATION_SCHEMAThe SQL standard defines INFORMATION_SCHEMA with standard table structures for portability.
12345678910111213141516171819202122232425262728293031323334353637383940
-- Standard INFORMATION_SCHEMA queries (SQL standard, cross-database) -- List all user tablesSELECT table_name, table_typeFROM information_schema.tablesWHERE table_schema NOT IN ('pg_catalog', 'information_schema'); -- Find all columns with their constraintsSELECT t.table_name, c.column_name, c.data_type, c.character_maximum_length, c.is_nullable, c.column_defaultFROM information_schema.tables tJOIN information_schema.columns c ON t.table_name = c.table_nameWHERE t.table_schema = 'public'ORDER BY t.table_name, c.ordinal_position; -- Find all primary keysSELECT tc.table_name, string_agg(kcu.column_name, ', ') AS pk_columnsFROM information_schema.table_constraints tcJOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_nameWHERE tc.constraint_type = 'PRIMARY KEY'GROUP BY tc.table_name; -- Check which tables reference a given tableSELECT tc.table_name AS referencing_table, ccu.table_name AS referenced_tableFROM information_schema.table_constraints tcJOIN information_schema.constraint_column_usage ccu ON tc.constraint_name = ccu.constraint_nameWHERE tc.constraint_type = 'FOREIGN KEY' AND ccu.table_name = 'employee'; -- find refs to 'employee'Rule 4 compliance enables: schema comparison tools, automatic documentation generators, ORM schema introspection, migration script generators, and dependency analysis. All these tools work because metadata IS data, queryable with standard SQL.
A relational system may support several languages and various modes of terminal use. However, there must be at least one language whose statements are expressible, per some well-defined syntax, as character strings and that is comprehensive in supporting all of the following items: data definition, view definition, data manipulation, integrity constraints, authorization, and transaction boundaries.
Interpretation
Rule 5 requires a unified language capable of all database operations. While multiple interfaces may exist (graphical tools, APIs, embedded SQL), at least one complete language must exist.
SQL Fulfills This Rule
SQL is the comprehensive data sublanguage that modern databases use:
Data Definition (DDL):
View Definition:
Data Manipulation (DML):
Integrity Constraints:
Authorization (DCL):
Transaction Control (TCL):
| Category | Purpose | Key Statements |
|---|---|---|
| DDL | Define structure | CREATE, ALTER, DROP, TRUNCATE |
| DML | Manipulate data | SELECT, INSERT, UPDATE, DELETE, MERGE |
| DCL | Control access | GRANT, REVOKE |
| TCL | Control transactions | BEGIN, COMMIT, ROLLBACK, SAVEPOINT |
| Views | Define virtual tables | CREATE VIEW, CREATE MATERIALIZED VIEW |
| Constraints | Enforce integrity | PRIMARY KEY, FOREIGN KEY, CHECK |
Character String Representation
Notably, Codd specified that statements must be expressible as character strings. This ensures:
This is why SQL is text-based rather than a graphical or binary protocol.
While SQL is the standard comprehensive sublanguage, rule 5 allows additional languages too. Many databases support JSON query languages (PostgreSQL's jsonpath), graph queries (Cypher via extensions), or specialized analytical languages (Window functions). These supplement but don't replace SQL's comprehensive role.
All views that are theoretically updatable are also updatable by the system.
Interpretation
Views provide logical data independence by presenting data differently than base tables. But if views are read-only, they're limited. Rule 6 requires that any view which CAN be updated (mathematically) SHOULD be updatable.
When is a View Updatable?
A view is potentially updatable when updates can be unambiguously translated to base table updates:
UPDATABLE views typically:
NON-UPDATABLE views typically:
-- UPDATABLE VIEW-- Simple projection of base tableCREATE VIEW active_employees ASSELECT emp_id, name, department, salaryFROM employeeWHERE is_active = true; -- This is updatable:UPDATE active_employeesSET salary = 50000WHERE emp_id = 1001;-- Translates to:-- UPDATE employee -- SET salary = 50000 -- WHERE emp_id = 1001; -- This INSERT works too:INSERT INTO active_employees (emp_id, name, department, salary)VALUES (1005, 'Eve', 'Sales', 60000);-- (is_active defaults or uses CHECK OPTION)-- NON-UPDATABLE VIEW-- Aggregation makes updates ambiguousCREATE VIEW dept_stats ASSELECT department, COUNT(*) AS emp_count, AVG(salary) AS avg_salaryFROM employeeGROUP BY department; -- This CANNOT be updated:UPDATE dept_statsSET avg_salary = 80000WHERE department = 'Engineering';-- ERROR: How do we change individual -- salaries to achieve avg of 80000? -- Similarly ambiguous:INSERT INTO dept_statsVALUES ('Marketing', 5, 70000);-- ERROR: What are the 5 employees?No database fully complies with Rule 6. The mathematical definition of 'theoretically updatable' is complex, and many edge cases exist. Modern databases support updatable views for simple cases and allow INSTEAD OF triggers for complex cases. This remains an area where theory exceeds practice.
The capability of handling a base relation or a derived relation (view) as a single operand applies not only to the retrieval of data but also to the insertion, update, and deletion of data.
Interpretation
Rule 7 requires set-at-a-time processing: the ability to operate on multiple rows as a single logical operation, not just row-by-row processing.
Set vs Procedural Processing
Pre-relational databases often required:
OPEN CURSOR
FOR EACH ROW:
READ ROW
MODIFY VALUES
WRITE ROW
CLOSE CURSOR
Relational databases allow:
UPDATE Employee SET salary = salary * 1.10 WHERE department = 'Engineering';
The entire set of matching rows is updated as ONE operation.
Benefits of Set Operations
123456789101112131415161718192021222324252627282930313233
-- Rule 7: Set-at-a-time operations -- Insert multiple rows in one statementINSERT INTO employee (emp_id, name, department, salary)VALUES (1001, 'Alice', 'Engineering', 95000), (1002, 'Bob', 'Marketing', 78000), (1003, 'Carol', 'Engineering', 102000); -- Update all matching rows at onceUPDATE employee SET salary = salary * 1.10WHERE department = 'Engineering' AND last_review_date < '2023-01-01';-- All matching employees get raises in ONE operation -- Delete all matching rows at once DELETE FROM employeeWHERE termination_date < CURRENT_DATE - INTERVAL '7 years';-- All terminated employees beyond retention period deleted -- Insert from query result (set-based)INSERT INTO employee_archive SELECT * FROM employee WHERE is_archived = true;-- Could move thousands of rows in one operation -- Complex set updateUPDATE products pSET p.price = p.price * 0.9WHERE p.category_id IN ( SELECT c.id FROM categories c WHERE c.name = 'Clearance');-- Discount all clearance products at onceA common anti-pattern is using cursors to loop through rows when a set-based approach would work. While cursors are sometimes necessary (complex procedural logic), prefer set operations when possible. They're not just more elegant—they're typically much faster because the optimizer can work with the entire operation.
Application programs and terminal activities remain logically unimpaired whenever any changes are made in either storage representations or access methods.
Interpretation
Physical data independence means applications don't break when the physical storage layer changes. You can:
...all WITHOUT modifying application code.
The Abstraction Boundary
Physical data independence is achieved by maintaining a clean boundary:
Application ← SQL → Logical Schema ← Mapping → Physical Storage
Applications see only the logical view (tables, columns, constraints). The database handles the mapping to physical storage internally.
| Physical Change | Purpose | App Impact |
|---|---|---|
| Add B-tree index | Speed up lookups | None (faster queries only) |
| Move table to SSD | Improve I/O performance | None (transparent) |
| Enable compression | Reduce storage | None (handled internally) |
| Partition a table | Manageability, performance | None (partitioning is transparent) |
| Change from heap to clustered | Optimize range scans | None (storage detail) |
| Add read replicas | Scale reads | None (or minimal config change) |
| Upgrade RAID configuration | Redundancy | None (OS/hardware level) |
Application Query (unchanged):
SELECT * FROM orders WHERE customer_id = 12345;
Physical Storage Evolution:
Year 1: Single disk, no index
→ Full table scan, 500ms
Year 2: Added B-tree index on customer_id
→ Index lookup, 5ms
Year 3: Table partitioned by date, on SSD
→ Partition pruning + index, 2ms
Year 4: Hot data in memory, cold on disk
→ In-memory lookup, 0.5msResult: Application code never changed!
The exact same SELECT statement became 1000x faster
through physical optimizations alone.
Physical data independence enabled this evolution
without any application modifications.When applications use index hints (FORCE INDEX, USE INDEX), they're violating physical data independence—the application now depends on specific indexes existing. Use hints sparingly and document them; they create coupling between application and physical schema.
Application programs and terminal activities remain logically unimpaired when information-preserving changes of any kind that theoretically permit unimpairment are made to the base tables.
Interpretation
Logical data independence is about protecting applications from schema changes that don't remove information. If you reorganize tables in a way that preserves all the data and relationships, applications should continue working.
"Information-Preserving" Changes
These changes reorganize without losing information:
Achieving Logical Independence with Views
Views are the primary mechanism for logical data independence:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
-- BEFORE: Original table structureCREATE TABLE person ( id INT PRIMARY KEY, name VARCHAR(100), home_address VARCHAR(200), work_address VARCHAR(200), home_phone VARCHAR(20), work_phone VARCHAR(20)); -- Applications use: SELECT name, home_address FROM person; -- AFTER: Normalized redesign (Rule 9 scenario)-- Split addresses and phones into separate tables CREATE TABLE person_v2 ( id INT PRIMARY KEY, name VARCHAR(100)); CREATE TABLE address ( id INT PRIMARY KEY, person_id INT REFERENCES person_v2(id), address_type VARCHAR(10), -- 'home' or 'work' address VARCHAR(200)); CREATE TABLE phone ( id INT PRIMARY KEY, person_id INT REFERENCES person_v2(id), phone_type VARCHAR(10), phone_number VARCHAR(20)); -- COMPATIBILITY VIEW: Old apps keep working!CREATE VIEW person ASSELECT p.id, p.name, ha.address AS home_address, wa.address AS work_address, hp.phone_number AS home_phone, wp.phone_number AS work_phoneFROM person_v2 pLEFT JOIN address ha ON p.id = ha.person_id AND ha.address_type = 'home'LEFT JOIN address wa ON p.id = wa.person_id AND wa.address_type = 'work'LEFT JOIN phone hp ON p.id = hp.person_id AND hp.phone_type = 'home'LEFT JOIN phone wp ON p.id = wp.person_id AND wp.phone_type = 'work'; -- Original query STILL WORKS:SELECT name, home_address FROM person; -- unchanged!When making breaking schema changes: (1) Create new structure, (2) Create view with old name pointing to new structure, (3) Migrate data, (4) Gradually update applications to use new structure directly, (5) Eventually remove compatibility view. This allows incremental migration without downtime.
Integrity constraints specific to a particular relational database must be definable in the relational data sublanguage and storable in the catalog, not in the application programs.
Interpretation
Rule 10 mandates that data integrity rules be:
Why Database-Level Constraints Matter
If constraints exist only in applications:
Database-enforced constraints are:
1234567891011121314151617181920212223242526272829303132333435363738394041
-- Integrity constraints defined in the database (Rule 10 compliant) CREATE TABLE employee ( emp_id INT PRIMARY KEY, -- Entity integrity email VARCHAR(100) UNIQUE NOT NULL, -- Not null + unique first_name VARCHAR(50) NOT NULL, -- Required field last_name VARCHAR(50) NOT NULL, hire_date DATE NOT NULL DEFAULT CURRENT_DATE, -- Default value salary DECIMAL(10,2) CHECK (salary > 0), -- Domain constraint manager_id INT REFERENCES employee(emp_id), -- Self-referential FK department_id INT NOT NULL REFERENCES department(id), -- Referential integrity -- Table-level constraint: manager must earn more CONSTRAINT mgr_salary_check CHECK ( manager_id IS NULL OR EXISTS ( SELECT 1 FROM employee m WHERE m.emp_id = manager_id AND m.salary > salary ) ) -- Note: This specific syntax varies by DBMS); -- Complex constraints via triggers when CHECK is insufficientCREATE OR REPLACE FUNCTION enforce_department_budget()RETURNS TRIGGER AS $$BEGIN IF (SELECT SUM(salary) FROM employee WHERE department_id = NEW.department_id) > (SELECT budget FROM department WHERE id = NEW.department_id) THEN RAISE EXCEPTION 'Salary would exceed department budget'; END IF; RETURN NEW;END;$$ LANGUAGE plpgsql; CREATE TRIGGER check_budget BEFORE INSERT OR UPDATE ON employee FOR EACH ROW EXECUTE FUNCTION enforce_department_budget(); -- Query constraints from catalogSELECT constraint_name, constraint_type, table_nameFROM information_schema.table_constraintsWHERE table_name = 'employee';In practice, many constraints ARE enforced in application code—for performance, complexity, or cross-system reasons. While not ideal per Rule 10, be pragmatic: use database constraints for fundamental integrity (keys, referential integrity, simple checks) and application logic for complex business rules that require procedural logic or external system access.
The end user must not be able to see that the data is distributed over various locations. Users should always get the impression that the data is located at one site only.
Interpretation
In distributed databases, data may be spread across multiple physical locations (nodes, data centers, regions). Rule 11 requires that this distribution be transparent to users and applications.
Location Transparency
Applications should access distributed data exactly as if it were local:
-- This query should work the same whether 'orders' is:
-- - Local table
-- - On another server in the same data center
-- - Partitioned across continents
-- - Replicated globally
SELECT * FROM orders WHERE customer_id = 12345;
The DBMS handles:
Types of Distribution Transparency
| Transparency Level | What's Hidden | Example Capability |
|---|---|---|
| Location | Physical node addresses | Query works regardless of data location |
| Fragmentation | How tables are partitioned | Query spanning partitions looks like one table |
| Replication | Multiple data copies | Reads served from any replica transparently |
| Latency | Network delays | Query optimizer considers network costs |
| Failure | Node outages | Automatic failover without app changes |
Modern Distributed Databases
Rule 11 was written in 1985 when distributed databases were rare. Today, distribution is common:
Modern systems (CockroachDB, Spanner, Aurora) provide strong distribution transparency. Others (some NoSQL systems) expose distribution for performance tuning, partially violating Rule 11 but sometimes necessarily.
Perfect distribution transparency conflicts with the CAP theorem (you can't have perfect Consistency, Availability, and Partition tolerance simultaneously). Real distributed databases make trade-offs. Highly transparent systems may sacrifice some availability or consistency during network partitions. Understanding these trade-offs is crucial for distributed system design.
If a relational system has a low-level (single-record-at-a-time) language, that low-level language cannot be used to subvert or bypass the integrity rules and constraints expressed in the higher-level relational language (multiple-records-at-a-time).
Interpretation
Rule 12 is about security and integrity: low-level interfaces cannot bypass high-level rules. Even if you have access to row-level APIs, internal functions, or direct storage access, you can't use these to:
Why This Matters
Databases often provide multiple access methods:
If low-level methods could bypass constraints, the integrity guarantees of the high-level SQL would be meaningless. A constraint is only as strong as the weakest access path.
Real-World Trade-offs
In practice, some Rule 12 violations are intentional for:
However, these should be:
The ultimate Rule 12 violation is editing database files directly (outside the DBMS). This bypasses all constraints, transactions, and integrity mechanisms. Never do this on a production system. Even for recovery, use DBMS-provided tools that maintain structural integrity. Direct file manipulation can corrupt data beyond recovery.
Codd articulated these rules in 1985—nearly 40 years ago. How do they hold up today?
Still Fully Relevant
Rules 0, 1, 2, 4, 5, 7, 8, 9, 10 remain as valid as ever. The core principles of:
These are timeless and define quality database design.
Evolved in Practice
Rule 3 (NULLs) remains challenging; three-valued logic continues to cause bugs. Rule 6 (Updatable views) is still only partially supported. Rules 11 and 12 have become more complex with distributed systems and multiple access methods.
| Rule | PostgreSQL | MySQL | SQL Server | Oracle |
|---|---|---|---|---|
| 0: Foundation | ✓ | Partial | ✓ | ✓ |
| 1: Information | ✓ | ✓ | ✓ | ✓ |
| 2: Guaranteed Access | ✓ | ✓ | ✓ | ✓ |
| 3: NULL Handling | ✓ | ✓ | ✓ | ✓ |
| 4: Online Catalog | ✓ | ✓ | ✓ | ✓ |
| 5: Comprehensive Language | ✓ | ✓ | ✓ | ✓ |
| 6: View Updating | Partial | Partial | Partial | Partial |
| 7: Set Operations | ✓ | ✓ | ✓ | ✓ |
| 8: Physical Independence | ✓ | ✓ | ✓ | ✓ |
| 9: Logical Independence | ✓ (via views) | ✓ | ✓ | ✓ |
| 10: Integrity Independence | ✓ | Partial | ✓ | ✓ |
| 11: Distribution Independence | ✓ (with extensions) | Partial | ✓ (with AG) | ✓ (RAC) |
| 12: Non-subversion | Mostly | Partial | Mostly | Mostly |
When evaluating databases (especially newer systems or NoSQL alternatives), Codd's rules provide a useful framework. Where does the system diverge from pure relational principles? Is that divergence intentional for valid reasons (performance, flexibility) or a limitation? Understanding the trade-offs helps make informed technology choices.
Codd's 12 Rules (plus Rule 0) represent the definitive criteria for what constitutes a truly relational database management system. Let's consolidate the key points:
What's Next:
Having explored Codd's formal rules, we'll now examine why the relational model achieved dominance in the database industry. The next page explores the factors that enabled the relational model to triumph over hierarchical and network alternatives, and what this means for modern database choices.
You now understand Codd's 12 Rules—the definitive criteria that distinguish truly relational databases from pretenders. These rules translate mathematical theory into practical requirements, providing a framework for evaluating databases and understanding what relational compliance means. Next, we'll explore why the relational model won the database wars.