AI 開發工作流

用 Claude 生一份 505 張表的資料字典:結構落差歸零,語意落差原封不動

2026-08-13 #Claude Code#Python#MCP#SQL Server#資料字典

一套內部系統的 SQL Server 資料庫有 505 張表、6868 個欄位,而手上的資料字典是一份人工維護的 Excel,涵蓋不到三分之一。

我用 Claude Code 接 MSSQL MCP 直接讀 schema,把整份字典重生了一次。結果很值得記錄:結構的部分(表名、欄位、型態、長度、PK、FK、index)補到 100%,但 6868 個欄位裡只有 27.9% 有中文名稱、3.4% 有備註說明——而後者才是決定這份文件有沒有人要看的東西。

這篇寫怎麼做的、落差的實際數字、以及這套做法要付的三個代價。文中的資料庫名、表名、檔名都已代換過,數字全部是實際跑出來的。

一、資料字典會死,是因為兩種落差

一份資料字典有兩種東西會過期,而且性質完全不同。

結構落差——DBA 上週加了三個欄位,文件裡沒有。這種落差是機械性的:答案就在 INFORMATION_SCHEMA 裡,任何時候都查得到,只是沒人有空去同步。

語意落差——一個叫 PROC_FLAG 的 char(1) 欄位到底代表什麼?Y 跟 1 有沒有差別?這個答案不在資料庫裡,它在當年寫這支程式的人腦袋裡。

傳統的手寫資料字典兩種落差一起吃:一次性寫完,結構立刻開始漂移,語意則從一開始就只補得到自己知道的那部分。然後文件失去可信度,沒人維護,正式死亡。

把這兩種落差分開看,才會發現它們該用完全不同的方式處理——這也是整篇文章的結論。

二、三步流水線

實際的做法是一條三步的 Python 流水線,openpyxl 是唯一的第三方依賴:

flowchart TB
    subgraph S["Schema 來源"]
        mcp["Claude Code
+ MSSQL MCP"] mcp --> raw["schema / PK 查詢結果
(JSON)"] end raw --> gen["gen_db_docs.py"] gen --> files["output/*.xlsx
505 個單表檔案"] files --> merge["merge_xlsx.py"] merge --> book["APPDB.xlsx
目錄頁 + 505 張工作表"] manual["既有人工文件
TableList.xlsx"] --> write["write_chinese.py"] book --> write write --> final["完成品:
機器結構 + 人工語意"]

Schema 不是自己寫 SQL 撈的,是讓 Claude 透過 MSSQL MCP server 的 list_tables / describe_table / read_data 查出來。MCP 這層有稽核日誌,logs/audit.jsonl 記下了每次呼叫:

1
2
{"timestamp":"2026-03-13T07:07:50.105Z","toolName":"read_data",
"result":{"success":true,"recordCount":6868},"durationMs":2446}

recordCount 是 6868——跟最後 Excel 裡數出來的欄位總數一模一樣。這種能對得起來的數字是後續一切統計的基礎,也是我願意相信這份產出的原因。

同一份日誌裡還有三次 QUERY_EXECUTION_FAILED。失敗一樣被記下來這件事本身有價值——查詢打歪了會留下痕跡,不會像一次性腳本那樣跑完就沒了。

為什麼先產 505 個檔案,再合併

gen_db_docs.py 不直接產出一份大 Excel,而是一張表一個 .xlsx,第二步才用 merge_xlsx.py 合併並加上目錄頁與超連結。

多這一步的理由很實際:單表可以獨立重產。某張表的欄位查漏了、格式不對,重跑那一張就好,不必整份 505 張表重來。合併腳本本身也明確不修改 output/ 底下的原檔,所以合併可以無限次重跑。

代價是合併這步不便宜:字型、填色、框線、對齊、數值格式都要逐格複製,openpyxl 沒有整表複製這種東西。505 張表乘以每張表的儲存格數,全部要跑過一遍。

三、真正的關鍵設計:只填空,絕不覆蓋

第三步 write_chinese.py 是整套東西唯一有巧思的地方,也是決定這份文件會不會活過三個月的關鍵。

它從既有的人工文件裡把中文表名、中文欄位名、備註抓出來,回填進機器產生的字典。核心規則只有一條:

1
2
3
4
5
6
7
if cn_name and row[3].value is None:   # D 欄空的才寫中文名
row[3].value = cn_name
col_written += 1

if comments and row[14].value is None: # O 欄空的才寫備註
row[14].value = comments
comment_written += 1

row[3].value is None 這個條件就是全部的重點:已經有內容的格子永遠不動。

為什麼這條規則這麼重要?因為它讓「重新產生」變成一個安全的操作。schema 變了就重跑一次流水線,人工補過的中文說明不會被機器蓋掉。少了這條,任何人在字典上補過的心血都會在下次重生時消失,而只要發生過一次,就不會再有人願意花時間補了——這正是大多數自動產生的文件最後沒人維護的死法。

腳本另外還會在動手前自動備份一份,這是把「不覆蓋」這個承諾再加一道保險。

對齊方式則相對脆弱:它靠 B 欄的項次序號把來源檔和目標檔的欄位對起來,不是靠欄位名稱。所以如果哪天欄位順序變了,中文名就會對到錯的欄位上——這是這套設計裡我最不放心的一處。

四、落差的實際數字

流水線跑完之後,把成品拉出來統計(openpyxl 開 read-only 掃 506 張工作表,約 3 秒):

項目 數量 覆蓋率
資料表 505 —
欄位總數 6868 —
結構資訊(型態、長度、NULL、PK、FK、index) 6868 100%
有中文表名的表 192 38.0%
有中文名稱的欄位 1916 27.9%
有備註說明的欄位 231 3.4%
欄位中文名全數補齊的表 86 17.0%
完全沒有任何中文說明的表 373 73.9%

單表最多 162 個欄位,平均 13.6 個。

這張表就是整件事的結論:機器把結構落差壓到零,語意落差幾乎原封不動。

回填率之所以只有這樣,是因為既有的人工文件本身就只涵蓋 137 張工作表、180 筆表名描述——來源就這麼多,回填再準確也生不出沒有的東西。

換句話說,這套工具沒有「產生」任何語意知識,它只是把散落在一份舊 Excel 裡的既有知識,準確地搬到一份結構完整的新文件上。這個定位要講清楚,不然很容易對 AI 產生錯誤的期待。

五、三個要老實承認的代價

1. 這條流水線現在跑不起來

gen_db_docs.py 的輸入寫死在兩個暫存檔路徑上:

1
2
SCHEMA_FILE = r'C:\Users\<user>\AppData\Local\Temp\1773385670199-copilot-tool-output-f1jwyc.txt'
PK_FILE = r'C:\Users\<user>\AppData\Local\Temp\1773385669986-copilot-tool-output-a5cm5z.txt'

那是 AI 工具查詢完之後 dump 出來的暫存輸出。寫這篇的時候我去確認了一下:

1
ls: cannot access '...1773385670199-copilot-tool-output-f1jwyc.txt': No such file or directory

檔案已經不在了,這支腳本現在跑不起來。

這是整件事最值得記下來的一個坑:把 AI 工具的暫存輸出當成 pipeline 的輸入,等於做出一個註定不可重現的東西。當下很順——反正檔案就在那裡,路徑複製貼上就能跑——但它的壽命只有幾天。

正確的做法是查詢結果一拿到就存進專案目錄,當成版控過的中繼產物。多一個步驟,換來這條流水線隨時可以重跑。

2. 有三類資訊是內聯在程式碼裡的常量

schema 和 PK 是從查詢結果讀的,但 FK、identity、index 這三類資訊是直接以 Python list 寫死在腳本裡的:

1
2
3
4
FK_DATA = [
{"TABLE_NAME":"<table>","COLUMN_NAME":"<column>"},
...
]

當下這樣做最快,但意思是換一個資料庫快照就要重查一次、重貼一次。這不是一條可以排程的 pipeline,是一次性的產出工具——認清這件事,比假裝它是自動化來得有用。

3. 三個腳本靠約定的儲存格座標協作

B4 是中文表名、第 5 列是表頭、資料從第 6 列開始、C 欄是欄位名、D 欄是中文名、O 欄是備註——這組座標同時被三個腳本硬編碼。改任何一處版面,三支程式都要跟著改,而且改漏了不會報錯,只會安靜地把中文名寫到錯的欄位去。

這是典型的隱性契約:能動,但沒有任何機制保護它。如果這套東西要長期維護,第一件該做的事是把版面定義抽成一個共用的常數模組。

六、所以什麼該交給 AI

跑完這一輪,我對「AI 幫忙寫文件」的看法變得比較具體:

結構 語意
事實在哪裡 資料庫裡,隨時查得到 人的腦袋、舊文件、程式碼慣例
AI 能做的 全量、精確、可重複產生 只能搬運既有的,生不出新的
落差怎麼消 重跑流水線 只能靠人補,沒有捷徑
這次的結果 100% 27.9%

所以真正的價值不在「AI 幫我寫了文件」,而在它把 6868 個欄位裡「還沒有人說得出它是什麼」的那 72% 清楚地標了出來。在此之前,沒有人知道這個缺口有多大;現在它是一個可以排進工作項目的具體數字,而且每補一格,下次重生都還會在。

這跟我之前比對兩版 DLL 的經驗是同一件事:機器負責把全量差異攤開來,人的價值在判斷哪些有意義。工具做的是把問題的規模量化,不是把問題解決掉。


同分類的其他筆記:

Creative Commons 姓名標示 非商業性

本文採用 CC BY-NC 4.0 授權

歡迎轉載與引用,請標明出處(用 Claude 生一份 505 張表的資料字典:結構落差歸零,語意落差原封不動 — mur mur);禁止用於商業用途。

商業使用或合作提案,歡迎來信洽談:[email protected]

留言
分享

留言