引言:一个看似简单但本质的问题
如果你问任何一个软件工程师”什么是 SQL?“,他们大概率能写出 SELECT * FROM users WHERE age > 18。但如果你追问”为什么 SQL 会诞生?它解决了什么问题?在它出现之前数据库是什么样的?“,大多数人可能就答不上来了。
理解一项技术的”为什么”比知道它”怎么做”重要得多。本文试图回答这个根本性的问题:关系数据库理论和 SQL 产生的历史动机是什么,它们到底解决了哪些核心问题?
前关系时代:混乱的数据管理
1960 年代的数据库现状
在 Codd 于 1970 年发表那篇划时代的论文《A Relational Model of Data for Large Shared Data Banks》之前,数据管理处于一个相当”原始”的状态。
当时的数据库系统主要分为两大阵营:
1. 层次模型(Hierarchical Model)—— IBM IMS 为代表
层次模型将数据组织为树状结构。每个记录只有一个父记录,数据通过指针(物理地址)相互链接。
Department (D01, "Engineering")
├── Employee (E001, "Alice", 80000)
│ ├── Child (C001, "Bob", 10)
│ └── Child (C002, "Carol", 7)
└── Employee (E002, "Dave", 75000)
└── Child (C003, "Eve", 5)
看似直观,但问题重重:
- 非对称查询陷阱:从 Department 查 Employee 很容易(沿着树往下走),但从 Employee 查所在的 Department 则非常困难(需要反向遍历或维护额外的索引)
- 数据冗余:如果一个员工属于多个部门,必须在每棵树下复制该员工记录
- 物理依赖:应用程序必须知道数据的物理存储路径——指针、磁盘块地址等
2. 网络模型(Network Model)—— CODASYL DBTG 为代表
网络模型试图通过允许任意节点间的多对多关系来弥补层次模型的不足。数据通过”系”(set)相互连接,每个系有一个主记录和多个属记录。
Supplier (S1, "Acme")
/ \
/ \
(S1-P1 supply) (S1-P2 supply)
/ \
Part (P1, "Bolt") Part (P2, "Nut")
\ /
\ /
(P1-S2 supply) (P2-S2 supply)
\ /
Supplier (S2, "BestCo")
虽然比层次模型灵活,但网络模型也有致命缺陷:
- 导航式访问:程序员必须像走迷宫一样,通过
FIND NEXT IN SET等命令一步步沿着指针遍历数据 - 程序脆弱性:任何一个数据结构的变更(如添加新的系类型)都会导致所有遍历该结构的应用程序崩溃
- 无声明式查询:查询”供应螺栓的所有供应商”需要写几十行导航代码,而非一行声明式语句
根本矛盾:程序与数据的紧耦合
回顾前关系时代,可以将问题归纳为一个核心矛盾:应用程序与数据的物理存储结构深度绑定。
Codd 在他的 1970 年论文中尖锐地指出:
“未来大型数据银行的用户必须受到保护,不必了解数据在机器内部的组织方式。”
当时的状况恰恰相反:
- 改变存储结构 → 必须重写应用程序
- 添加新索引 → 必须修改查询逻辑
- 数据重组 → 牵一发动全身
这就是 Codd 试图解决的根本问题:数据独立性。
Codd 的革命:关系模型
核心洞察:用数学代替指针
Codd 的天才之处在于,他意识到可以用一个简单的数学抽象——关系(relation)——来完全替代复杂的指针网络。
关系就是一个集合论中的 n 元关系,形式上定义如下:
给定集合 (称为域),一个关系 是笛卡尔积的子集:
用数据库术语来说:
- 每个 是一个列的类型(如 INTEGER、VARCHAR)
- 是一个表
- 笛卡尔积中的每个元素 是一个行(元组)
这个看似简单的定义带来了革命性的后果。
关系模型的五大杀手锏
1. 数据独立性(Data Independence)
这是 Codd 最核心的诉求。关系模型中,用户看到的是逻辑层的表,而不是物理层的指针。你可以随意改变底层的索引、存储布局、文件组织,而不需要修改任何应用程序代码。
类比:关系模型之于层次/网络模型,就像高级语言之于汇编语言。你用 SELECT 表达”想要什么”,而不是用指针遍历来指定”怎么做”。
2. 闭包性质(Closure)
关系代数的每个运算符的输入是关系,输出也是关系。这意味着关系代数表达式可以任意嵌套:
闭包性质是声明式查询语言的理论基础——因为每个子表达式的结果仍然是一个关系,你可以像搭积木一样构建任意复杂的查询。
3. 数据完整性(Data Integrity)
关系模型提供了两种内置的一致性机制:
- 实体完整性(Entity Integrity):每个关系必须有一个主键,主键不能为 NULL
- 参照完整性(Referential Integrity):外键必须引用存在的主键值
在前关系时代,这类约束完全由应用程序代码来保证,极易出错。关系模型将这些约束提升到了数据库系统级别,成为全局保证。
4. 消除冗余——规范化理论(Normalization)
Codd 提出了第 1、2、3 范式(后来由 Boyce 等人扩展到 BCNF)。核心思想是:
将数据分解为”一个事实,一个地方”的规范化结构,避免插入异常、删除异常和更新异常。
例如,将带有嵌入式 course 信息的 student 表:
| student_id | name | course_id | course_name | instructor |
|---|---|---|---|---|
| 001 | Alice | CS101 | Database | Dr. Smith |
| 001 | Alice | CS202 | Algorithms | Dr. Jones |
| 002 | Bob | CS101 | Database | Dr. Smith |
分解为三个独立的规范化关系:
- Students: (student_id, name)
- Courses: (course_id, course_name, instructor)
- Enrollments: (student_id, course_id)
这样,修改课程名称只需要更新一行,而不会出现数据不一致。
5. 声明式查询语言——关系演算(Relational Calculus)
Codd 同时提出了关系代数和关系演算。关系演算基于一阶谓词逻辑,允许用户以声明式的方式描述想要的数据:
这等价于 SQL 的 SELECT * FROM Employee WHERE age > 18。用户只声明”要什么”,由系统决定”怎么取”。
Codd 证明了关系代数与关系演算的等价性,为查询优化器奠定了理论基础——优化器可以将声明式查询重写为等价的、更高效的关系代数表达式。
SQL 的诞生:理论标准化为工程实践
System R 与 SEQUEL
1974 年,在 Codd 的论文发表 4 年后,IBM 圣何塞实验室的 Don Chamberlin 和 Ray Boyce 发表了《SEQUEL: A Structured English Query Language》。SEQUEL 后来因商标原因改名为 SQL。
他们设计 SQL 的初心非常明确:
“为没有数学或计算机编程背景的用户提供一种访问关系数据库的方式。”
System R 项目是 IBM 的第一个关系数据库原型。它在工程上验证了 Codd 的理论:
- 查询优化器(基于代价的优化)——证明声明式查询可以高效执行
- 事务管理(ACID)——证明了多用户并发访问的可行性
- 视图(View)——实现了逻辑数据独立性的承诺
为什么是”结构化英语”而非纯数学符号?
这是一个关键的设计选择。关系演算虽然优雅,但 对普通用户来说太难了。
SQL 选择了类自然语言的语法:
SELECT t.*
FROM R AS t, R AS u
WHERE t.a = u.b AND u.c > 100;
这一选择极大地降低了使用门槛,是 SQL 得以普及的关键。但同时也埋下了 SQL 与纯关系模型偏离的种子(如允许重复行、三值逻辑等),C. J. Date 在《SQL and Relational Theory》中对此有详尽批评。
为什么至今不可替代?
1. 数据独立性仍是刚需
无论是 MySQL 换成 TiDB,还是从单机迁移到分布式,只要上层是 SQL 接口,应用程序代码几乎不需要修改。这种抽象层的价值怎么强调都不为过。
2. 声明式查询是终局设计
命令式数据库访问(如早期的 CODASYL 导航式查询、当今的某些 NoSQL API)在简单场景下可能更灵活,但一旦查询逻辑变得复杂,声明式的优势就无限放大。
考虑这个对比:
导航式(伪代码):
for each department:
for each employee in department:
if employee.salary > avg(department.salary):
print employee.name
声明式(SQL):
SELECT e.name
FROM employee e
JOIN (
SELECT dept_id, AVG(salary) AS avg_sal
FROM employee
GROUP BY dept_id
) d ON e.dept_id = d.dept_id
WHERE e.salary > d.avg_sal;
当需求变更(如加上分区过滤、多表关联),声明式的改动远小于命令式。
3. 优化器自由度的价值
声明式查询给查询优化器留下了巨大的优化空间。过去 50 年,数据库社区在查询优化上投入的研究可能比任何其他单一数据库子领域都多:
- 从 System R 的基于代价的优化器
- 到火山模型和向量化执行
- 到现代的分析型系统中的大规模并行执行
- 到基于机器学习的查询优化
所有这些优化的受益者,是最原始那句 SELECT ... WHERE ...。这就是抽象的力量。
4. 事务与一致性保障
ACID 不是关系模型的原始组成部分,但 SQL 数据库将事务概念与关系模型紧密结合。这使得关系型数据库成为”记录系统”(System of Record)的首选——金融系统、ERP、电商订单,任何不能丢数据的场景。
关系模型的边界与反思
承认关系模型的伟大之处,也需要认识到它的局限:
1. 数据模型的单一性 并非所有数据都适合用表格表达。图结构数据(社交网络)、文档数据(JSON)、时序数据(传感器)用关系模型建模时,要么不自然,要么性能差。
2. Schema 的刚性 强类型、预定义 Schema 是关系模型的数据完整性保证来源,但也意味着灵活性不足。这是 MongoDB 等文档数据库存在的理由——在 Schema 不确定的场景下,文档模型更加便捷。
3. 扩展性的挑战 关系模型的 ACID 语义在分布式环境下实现极其困难(CAP 定理)。这催生了 NewSQL(如 CockroachDB、TiDB)和 NoSQL 运动。
但有趣的是,大多数 NoSQL 数据库最终都”长”出了 SQL 接口(Cassandra 的 CQL、MongoDB 的聚合管道、甚至 Redis 的类似 SQL 的查询)。这不是巧合——声明式数据访问是表意清晰性和系统优化空间的帕累托最优解。
结语:那篇 11 页论文的遗产
1970 年,Edgar F. Codd 发表了一篇 11 页的论文。它没有提出任何新算法,没有发明新的数据结构,甚至没有附上性能对比。它只做了一件事:用一个简洁优雅的数学框架,重新定义了人类与数据交互的方式。
50 多年后的今天,我们仍然在:
- 用
SELECT查询——源自关系演算的声明式思维 - 用
JOIN连接——关系代数的自然连接 - 用主键和外键——实体完整性和参照完整性
- 用范式设计表结构——规范化理论
每一次你写下一行 SQL,都是对一个伟大理论的不自觉致敬。
参考文献
- Codd, E. F. (1970). “A Relational Model of Data for Large Shared Data Banks.” Communications of the ACM, 13(6), 377–387.
- Chamberlin, D. D., & Boyce, R. F. (1974). “SEQUEL: A Structured English Query Language.” Proceedings of the 1974 ACM SIGFIDET Workshop.
- Date, C. J. (2009). SQL and Relational Theory: How to Write Accurate SQL Code. O’Reilly Media.
- Astrahan, M. M., et al. (1976). “System R: Relational Approach to Database Management.” ACM Transactions on Database Systems, 1(2), 97–137.
- Stonebraker, M., & Hellerstein, J. M. (2005). Readings in Database Systems (4th ed.). MIT Press.
- Codd, E. F. (1972). “Further Normalization of the Data Base Relational Model.” Data Base Systems, Courant Computer Science Symposia Series, Vol. 6.