1969 - Bachman 網路模型(集合關係與導航式資料庫)#
案件摘要#
1969 年,Charles W. Bachman(1924–2017)——在 GE 開發過 IDS (Integrated Data Store,1963 上線,第一個網路資料庫)的老偵探—— 把他的辦案手法整理成一套網路資料模型, 並由 CODASYL(COBOL 的標準委員會)成立 DBTG (Data Base Task Group)著手標準化(1971 年報告定案)。 他的破案關鍵只有一個,卻直搗層次模型的死穴:
用「集合(set)」表示一對多的連結,用「導航(navigational)」 沿指標走訪資料——程式設計師親自當領航員。 1973 年,Bachman 獲圖靈獎——首位資料庫得主, 演說題目一針見血:《The Programmer as Navigator》(程式設計師即領航員)。
前因 -- 為什麼會有這個案子#
- 計算的理論基礎(1936): 資料庫是圖靈機思想(程式 = 資料)的工程實現 (見
../資訊科學/1936-圖靈機.md)——
- 層次模型的死穴(1961): IMS 證明了「一份資料、多個程式共用」可行(見
1961-IBM層次資料庫.md), 但留下了兩條懸案:
例如:同一個「軸承」由兩家供應商供貨——樹只能複製兩份, 資料重複、更新異常。
- GE 的 IDS(1963): Bachman 在奇異公司(General Electric)為製造業開發 IDS—— 第一個用「指標鏈結」組織資料的系統,先於標準問世。
- 演算法的時代精神(1968): Knuth 的《The Art of Computer Programming》第一卷 (見
../資訊科學/1969-克努特演算法藝術.md)把指標、鏈結串列、樹 的操作整理成嚴謹的數學——
同時 CODASYL 正在標準化 COBOL,商業資料處理需要一個標準的資料庫介面。
線索與推理 -- 數學式、程式、理論#
第一條線索:網路(圖)vs 樹#
網路模型 = 一個有向圖 \(G = (V, E)\) : - \(V\) :記錄(record)的集合; - \(E \subseteq V \times V\) :連結(link)——一筆記錄可以有多個父親。
第二條線索:集合(set)型記錄關係#
Bachman 的核心發明——set(集合): - 一個 set = 一個首記錄(owner) + 若干成員記錄(member); - 成員之間用指標鏈串起來(循環鏈結串列)。
多對多 = 同一筆記錄參與多個 set:
例如「軸承 \(P_2\) 」同時是 \(S_1\) (供應商一供貨)與 \(S_2\) (供應商二供貨)的成員—— 不用複製資料!
第三條線索:導航式導覽(navigational)#
CODASYL 的基本操作(DBTG 報告,1971 定案): - FIND:依鍵值或位置定位一筆記錄(游標停在這裡); - GET NEXT:沿 set 指標走到下一個成員; - GET OWNER:沿指標反向找到首記錄; - INSERT / REMOVE:把成員加入或移出 set。
導航式查詢 = 一連串的 FIND → GET NEXT 指令:
代價:
這是「邏輯資料相依」的殘留——層次模型的病沒有根治,只是緩解。
Python:小型網路資料庫與導航式查詢#
# Bachman 網路資料庫:記錄(record)+ 集合(set)連結 + 導航式查詢
class Record:
"""一筆網路資料庫記錄:資料欄位 + 它參與的所有集合連結"""
def __init__(self, key, **fields):
self.key = key
self.fields = fields
self.members = [] # 我當「首記錄」,指向我的成員(set 的一對多)
def connect(self, *records):
for r in records:
self.members.append(r) # 建 set:self 為 owner,r 為 member
return self
class NetworkDB:
"""網路資料庫:所有記錄 + FIND / GET NEXT 的導航式操作"""
def __init__(self):
self.records = {}
def add(self, rec):
self.records[rec.key] = rec
return rec
def find(self, key):
"""FIND:依鍵值定位記錄(導航的起點)"""
return self.records[key]
def get_next(self, owner, i):
"""GET NEXT:沿 set 指標走到下一個成員"""
if i < len(owner.members):
return owner.members[i]
return None
# ---- 建立資料庫:供應商—零件(多對多網路,不是樹!)----
db = NetworkDB()
s1 = db.add(Record("S1", city="台北"))
s2 = db.add(Record("S2", city="高雄"))
s3 = db.add(Record("S3", city="台中"))
p1 = db.add(Record("P1", name="螺絲"))
p2 = db.add(Record("P2", name="軸承"))
p3 = db.add(Record("P3", name="齒輪"))
# 同一筆記錄可以出現在多個 set 中 → 網路(圖),不是樹
s1.connect(p1, p2)
s2.connect(p2, p3)
s3.connect(p1, p3)
# ---- 查詢 1:FIND + GET NEXT 導航式遍歷 ----
print("查詢 1:導航式遍歷(FIND + GET NEXT)")
for skey in ["S1", "S2", "S3"]:
s = db.find(skey)
parts = []
i, m = 0, db.get_next(s, 0)
while m is not None: # 沿指標導航
parts.append(m.key)
i += 1
m = db.get_next(s, i)
print(f" FIND {skey}({s.fields['city']})→ GET NEXT → {parts}")
# ---- 查詢 2:反向導航(零件 → 供應商)----
print("\n查詢 2:反向導航需要另一組指標(多對多的代價)")
print(" P2 的供應商:S1(台北)、S2(高雄) ← 需維護反向集合指標")
# ---- 查詢 3:驗證這是「圖」不是「樹」----
print("\n查詢 3:結構驗證——同一記錄有多個父親(網路 vs 樹)")
parent_count = {k: 0 for k in db.records}
for rec in db.records.values():
for m in rec.members:
parent_count[m.key] += 1
for k, n in parent_count.items():
kind = "樹(一個父親)" if n <= 1 else "網路(多個首記錄)"
print(f" 記錄 {k}:被 {n} 條 set 指到 → {kind}")
# ---- 結案的伏筆 ----
print("\n伏筆:導航式查詢的代價")
print(" 程式設計師必須親自寫出 FIND → GET NEXT 的每一步路徑,")
print(" 資料路徑改一個位元,程式就全面崩潰。")
print(" → 「能否用數學取代導航?」——這是 Codd(1970)要偵辦的下一案。")
查詢 1:導航式遍歷(FIND + GET NEXT)
FIND S1(台北)→ GET NEXT → ['P1', 'P2']
FIND S2(高雄)→ GET NEXT → ['P2', 'P3']
FIND S3(台中)→ GET NEXT → ['P1', 'P3']
查詢 2:反向導航需要另一組指標(多對多的代價)
P2 的供應商:S1(台北)、S2(高雄) ← 需維護反向集合指標
查詢 3:結構驗證——同一記錄有多個父親(網路 vs 樹)
記錄 S1:被 0 條 set 指到 → 樹(一個父親)
記錄 S2:被 0 條 set 指到 → 樹(一個父親)
記錄 S3:被 0 條 set 指到 → 樹(一個父親)
記錄 P1:被 2 條 set 指到 → 網路(多個首記錄)
記錄 P2:被 2 條 set 指到 → 網路(多個首記錄)
記錄 P3:被 2 條 set 指到 → 網路(多個首記錄)
伏筆:導航式查詢的代價
程式設計師必須親自寫出 FIND → GET NEXT 的每一步路徑,
資料路徑改一個位元,程式就全面崩潰。
→ 「能否用數學取代導航?」——這是 Codd(1970)要偵辦的下一案。
結案 -- 後果與影響#
- 標準的誕生(1971):
1970 年代,幾乎所有大廠(IBM 之外的 Honeywell、Univac、CINCOM 等) 都推出 CODASYL 系統——網路模型統治了 1970 年代的資料庫市場。 - 首位資料庫圖靈獎得主(1973):
演說《The Programmer as Navigator》宣告: 程式設計師的新身分是資料世界的領航員—— 這既是網路模型的宣言,也精準暴露了它的問題。 - 路線之爭:Bachman vs Codd:
- Bachman:資料有結構,程式沿結構走——效率與實務;
- Codd:程式只說「要什麼」,不說「怎麼走」——獨立與簡單 (見
1970-科德關聯式模型.md)。 這場論戰以 Codd 的勝利告終,但導航式思維從未消失—— 它轉世為物件導向資料庫、圖資料庫(Neo4j)、NoSQL。 - 複雜性為關聯式革命鋪路:
CODASYL 系統的查詢程式碼冗長且易碎—— 正是這種複雜性,讓 Codd 的「用數學取代導航」一擊中的。 - 歷史定位:Bachman 是資料庫史的第二章—— 他把樹升級成圖、把資料連結標準化, 也用一場圖靈獎演說,親手寫下了自己時代的第一份「辯護狀」與「起訴書」。
關鍵人物與文獻#
- Charles W. Bachman(1924–2017):IDS(1963);CODASYL/DBTG 報告(1971); 《The Programmer as Navigator》(1973 圖靈獎演說)。
- CODASYL / DBTG:網路資料模型的標準化組織(1971 定案)。
- General Electric / Honeywell:IDS 的開發與後續商品化。
- E. F. Codd:關聯式模型(1970)——路線之爭的對手(見
1970-科德關聯式模型.md)。 - 相關案件:
../資訊科學/1936-圖靈機.md、1961-IBM層次資料庫.md、../資訊科學/1969-克努特演算法藝術.md。