1970 - Codd 的關聯式模型(資料庫的數學化)#
案件摘要#
1970 年,Edgar F. Codd(1923–2003) 在《ACM 通訊》發表 A Relational Model of Data for Large Shared Data Banks:
關聯式模型的核心: - 資料儲存在「表格」中(行 = tuple,列 = attribute); - 操作 = 關係代數(relational algebra):選擇、投影、連接、聯集; - 資料與程式分離——查詢用宣告式語言(後來的 SQL)。
1981 年,Codd 獲得圖靈獎—— 今天所有的資料庫(Oracle、MySQL、PostgreSQL、SQL Server)都是 Codd 模型的後代。
前因 -- 為什麼會有這個案子#
- Codd 的背景(1949–1970): Codd 是英國人,二戰後移居美國,在 IBM 工作(1963 年起)。 他是數學家出身(密西根大學的博士,通訊與邏輯)—— 這個背景決定了他的思路:資料庫應該有數學基礎。
- 資料庫的混亂(1960s): 當時的資料庫是階層式(hierarchical,如 IMS)或網狀式(network,如 CODASYL):
- 程式必須知道資料的「物理儲存方式」(指標、鏈結);
- 換一種儲存方式 → 程式全部重寫!
- Codd 的洞察(1970): 資料本來就是「集合」——表格只是集合的一種視覺化:
程式不需要知道儲存方式——只需要知道「邏輯結構」(表格)。
- IBM 的冷落(1970–1977): IBM 已投資 IMS(階層式資料庫,營收數十億)—— Codd 的模型威脅了 IMS 的存在! IBM 延遲了關聯式資料庫的研發—— 這給了 Larry Ellison 機會:Oracle(1977)第一個推出商用關聯式資料庫, 打敗了 IBM。
線索與推理 -- 數學式、程式、理論#
第一條線索:關聯式模型的形式定義#
關聯(relation): \(R \subseteq A_1 \times \cdots \times A_n\) (tuple 的集合)。 鍵(key):唯一識別 tuple 的屬性子集: - 主鍵(primary key):每個關聯一個; - 外鍵(foreign key):引用其他關聯的主鍵——關聯之間的連結。 正規化(normalization,1971): Codd 1971 年提出正規化形式,消除資料的冗餘: - 1NF:屬性不可再分(原子性); - 2NF:非鍵屬性完全依賴主鍵(消除部分依賴); - 3NF:非鍵屬性不傳遞依賴主鍵(消除傳遞依賴)。
第二條線索:關係代數(操作的數學)#
Codd 的關係代數(8 個操作): | 操作 | 記號 | 語義 | SQL 對應 | |------|------|------|---------| | 選擇 | \(\sigma_p(R)\) | 取滿足條件的行 | WHERE | | 投影 | \(\pi_A(R)\) | 取部分列 | SELECT A | | 連接 | \(R \bowtie S\) | 兩表的合併 | JOIN | | 聯集 | \(R \cup S\) | 行的合併 | UNION | | 交集 | \(R \cap S\) | 共同的行 | INTERSECT | | 差集 | \(R - S\) | 減去 | EXCEPT | | 笛卡兒積 | \(R \times S\) | 所有組合 | (JOIN 的基礎) | | 除法 | \(R \div S\) | 「對所有」查詢 | (NOT EXISTS) |
第三條線索:宣告式查詢與 SQL(1974)#
SQL(SEQUEL, 1974,Chamberlin 與 Boyce):
SELECT name, salary
FROM employees
WHERE dept = 'research' AND salary > 50000;
查詢最佳化器(query optimizer): 資料庫自動選擇最快的執行計畫—— 這是「程式不需要知道物理」的實現。
Python:關聯式模型的數值驗證#
import numpy as np
# 1) 關聯 = tuple 的集合(關聯式模型的核心)
employees = [
("E1", "張三", "research", 60000),
("E2", "李四", "sales", 45000),
("E3", "王五", "research", 55000),
("E4", "趙六", "sales", 48000),
]
departments = [
("research", "研究部"),
("sales", "銷售部"),
]
# 2) 關係代數的操作
def select(rel, pred):
"""σ:選擇(WHERE)"""
return [t for t in rel if pred(t)]
def project(rel, *attrs):
"""π:投影(SELECT)"""
idx = [list(attrs)] if False else None
return [tuple(t[i] for i in attrs) for t in rel]
def join(rel1, rel2, key1, key2):
"""⋈:自然連接(用鍵合併)"""
result = []
for t1 in rel1:
for t2 in rel2:
if t1[key1] == t2[key2]:
result.append(t1 + t2[1:]) # 避免重複鍵
return result
print("關係代數(Codd 1970):")
# σ:research 部門
r1 = select(employees, lambda t: t[2] == "research")
print(f" σ(dept=research):{r1}")
# π:姓名與薪水
r2 = project(employees, 1, 3)
print(f" π(name, salary):{r2}")
# ⋈:員工連接部門
r3 = join(employees, departments, 2, 0)
print(f" ⋈(員工 ⋈ 部門):")
for t in r3:
print(f" {t}")
# 3) SQL 的等價查詢(宣告式)
print("\nSQL 等價(宣告式查詢):")
print(" SELECT e.name, d.dept_name")
print(" FROM employees e JOIN departments d ON e.dept = d.dept_id")
print(" WHERE e.salary > 50000;")
# 執行等價的關係代數
r4 = select(join(employees, departments, 2, 0), lambda t: t[3] > 50000)
print(" 執行結果:")
for t in r4:
print(f" {t}")
# 4) 正規化的驗證(3NF:消除傳遞依賴)
print("\n正規化(3NF,1971):")
# 未正規化:員工表含部門名稱(傳遞依賴:E → dept_id → dept_name)
unnormalized = [
("E1", "張三", "research", "研究部", 60000),
("E2", "李四", "sales", "銷售部", 45000),
("E3", "王五", "research", "研究部", 55000),
]
# 檢查冗餘:research/研究部 重複
dept_names = [t[2:4] for t in unnormalized]
duplicates = len(dept_names) - len(set(dept_names))
print(f" 未正規化({len(unnormalized)} 行):部門名稱冗餘 {duplicates} 處")
# 正規化後:分成兩表
print(f" 正規化(3NF):員工表 {len(employees)} 行 + 部門表 {len(departments)} 行")
print(" → 消除冗餘:改部門名稱只需改 1 行(不是 2 行)")
# 5) 資料獨立性:換儲存方式不影響查詢
print("\n資料獨立性(Codd 的核心思想):")
print(" 程式只知道:表格(邏輯結構)")
print(" 程式不知道:B 樹、雜湊、磁碟佈局(物理結構)")
print(" → 換儲存方式(如加索引)不需要改查詢")
print(" → 這是「邏輯與物理分離」的實現(對照 1960s 的指標迷宮)")
關係代數(Codd 1970):
σ(dept=research):[('E1', '張三', 'research', 60000), ('E3', '王五', 'research', 55000)]
π(name, salary):[('張三', 60000), ('李四', 45000), ('王五', 55000), ('趙六', 48000)]
⋈(員工 ⋈ 部門):
('E1', '張三', 'research', 60000, '研究部')
('E2', '李四', 'sales', 45000, '銷售部')
('E3', '王五', 'research', 55000, '研究部')
('E4', '趙六', 'sales', 48000, '銷售部')
SQL 等價(宣告式查詢):
SELECT e.name, d.dept_name
FROM employees e JOIN departments d ON e.dept = d.dept_id
WHERE e.salary > 50000;
執行結果:
('E1', '張三', 'research', 60000, '研究部')
('E3', '王五', 'research', 55000, '研究部')
正規化(3NF,1971):
未正規化(3 行):部門名稱冗餘 1 處
正規化(3NF):員工表 4 行 + 部門表 2 行
→ 消除冗餘:改部門名稱只需改 1 行(不是 2 行)
資料獨立性(Codd 的核心思想):
程式只知道:表格(邏輯結構)
程式不知道:B 樹、雜湊、磁碟佈局(物理結構)
→ 換儲存方式(如加索引)不需要改查詢
→ 這是「邏輯與物理分離」的實現(對照 1960s 的指標迷宮)
結案 -- 後果與影響#
- 資料庫工業的誕生(1970–1980):
- System R(1974–1979,IBM):IBM 的原型(SQL 的誕生地);
- Ingres(1974–1977,Stonebraker,2014 圖靈獎):加州大學的原型—— PostgreSQL 的祖先;
- Oracle(1977,Ellison):第一個商用關聯式資料庫—— 打敗了冷落 Codd 的 IBM。
- SQL 的世紀(1974–):
SQL 至今是資料庫的通用語言—— 每天數十億次的查詢都用 SQL—— 這是資訊科學中「壽命最長」的語言之一。 - 資料獨立性的哲學(1970–):
Codd 的思想影響了整個軟體工程: - 分層架構:應用層、邏輯層、儲存層; - 介面與實作分離(1972 Parnas 的模組化); - 抽象化是軟體工程的核心(Dijkstra 1968 的結構化 + Codd 1970 的資料獨立)。 - Stonebraker 的後續(2014 圖靈獎): Michael Stonebraker(Ingres 與 Postgres 的創造者): - Postgres(1986):物件關聯式資料庫——PostgreSQL 的前身; - C-Store(2005):欄式儲存——分析型資料庫(Vertica); - H-Store(2007):記憶體資料庫——VoltDB; 2014 年圖靈獎——「現代資料庫系統」。 - NoSQL 的反叛(2000s)與 SQL 的回歸(2010s): - NoSQL(2000s):Google Bigtable(2006)、Amazon Dynamo(2007)—— 為了規模而犧牲關聯式; - NewSQL(2010s):Google Spanner(2012)——關聯式 + 規模; - 現代:關聯式模型依然是資料的核心—— Codd 的數學基礎是「最耐久」的。 - Codd 的晚年(1981 圖靈獎): Codd 1981 年獲圖靈獎,演講主題是關聯式模型的健康度檢查—— 他批評了打著「關聯式」旗號但不符合模型的產品—— Codd 的 12 條規則(1985)是「真正的關聯式資料庫」的檢驗標準。 - 歷史定位:Codd 是「資料庫的數學化」的人——
一句話:資料是數學,不是檔案。
關鍵人物與文獻#
- E. F. Codd:A Relational Model of Data for Large Shared Data Banks(1970);正規化(1971);12 條規則(1985);1981 圖靈獎。
- D. Chamberlin & R. Boyce:SEQUEL(1974)——SQL 的誕生。
- M. Stonebraker:Ingres(1974);Postgres(1986);2014 圖靈獎。
- C. J. Date:An Introduction to Database Systems(1975)——資料庫的教科書。
- 相關案件:
1973-UNIX作業系統.md、1968-結構化程式設計.md、1982-蘭波特Paxos.md。