問題現場
每隔一段時間,晶片供應商就用 Email 夾一份 Excel,回報一批 issue。原本就是有人收信、開 Excel、自己比對——這樣其實沒什麼不對,直到你想查點東西。
痛點很具體:沒有單一可查詢的來源。要查「某顆晶片的某類問題現在什麼狀態」得翻信箱、翻多份 Excel;要做趨勢、要篩選、要長期維護幾乎不可能;換人接手就得重新熟悉一遍別人的 Excel 排版習慣。
最小技術背景
- ETL:把資料從來源抽出來(Extract)、整理成統一格式(Transform)、再存進資料庫(Load)。
- upsert:寫入資料庫時,「有就更新、沒有就新增」,用一個 key 判斷是否同一筆,藉此天然去重。
- DataTables:前端表格元件,提供搜尋、排序、分頁。
- filter text / display text:同一欄可以有「畫面顯示的字」和「拿來篩選比對的字」兩種用途——把它們分開,才不會互相污染。
系統架構與資料流
flowchart LR OUT["Outlook 信件"] --> EX["Excel 附件"] EX --> ETL["ETL|抽取 + 正規化"] ETL --> UP["MySQL upsert|以 key 去重"] UP --> WEB["Flask + DataTables 看板"] WEB --> USER["可查詢 / 篩選 / 維護"]
關鍵設計決策(含取捨)
| 決策 | 選了什麼 | 否決的替代 | 為什麼 |
|---|---|---|---|
| 流程模型 | 串成一條 ETL 連續體,每站職責分明 | 每次手動處理一份 Excel | 否決:查不了、累積不起來、也沒法長期維護 |
| 篩選語意 | 顯示文字與篩選文字分離 | 直接拿顯示字去篩 | 否決:畫面要顯示的字、跟拿來比對的字,會互相污染 |
| 比對方式 | 精準比對(exact match) | 子字串比對(contains) | 否決:篩 Open 會把 Open - Pending 一起撈出來 |
| 設定管理 | ETL 與 Web 共用同一份 DB 連線設定 | 兩邊各抄一份 | 否決:兩份設定遲早各走各的,難維護 |
代表性技術證據
顯示與篩選分離(pseudo-code,示意)
row = {
"status_display": "Open - Pending", # 畫面上看到的那個字
"status_filter": "Open - Pending", # 拿來篩選比對的,跟顯示的分開存
}
def matches(row, query):
return row["status_filter"] == query # 整個對起來才算,不是用 contains 比子字串
# 所以篩 "Open",不會把 "Open - Pending" 一起撈進來
ETL 去重(pseudo-code,示意)
# 拿 (來源, issue_id) 當 key,重跑只會更新,不會多長出重複列
db.upsert("issues", key=["source", "issue_id"], row=normalized)
踩過的坑
| 欄位 | 內容 |
|---|---|
| 症狀 | 篩選 Open,結果跑出一堆 Open - Pending |
| 初判(猜錯) | 第一個念頭是匯入的資料髒了 |
| 驗證 | 檢查篩選邏輯,發現用的是子字串比對 |
| 根因 | filter 用 contains,前綴相同就誤命中 |
| 修法 | 改成精準比對,並把顯示文字與篩選文字分離 |
| 預防 | 加入「前綴相同的相似狀態」測試案例(Open vs Open - Pending) |
驗證方式
- 去重:ETL 連跑兩次,資料庫列數不增加(upsert key 生效)。
- 篩選:對相似前綴狀態做測試,確認精準比對不誤命中。
- 設定:ETL 與 Web 取得的 DB 連線來自同一份設定(無第二份可漂移)。
- 驗收標準:同一批 Email/Excel 重匯不產生重複;篩選結果與預期一致。
可複製 SOP:把週期性報表流程工程化
適用情境:定期以 Email / Excel 收到、需要長期查詢與維護的資料。
不適用情境:一次性、用完即丟的資料,不值得建 ETL。
前置條件
- 找出每筆資料的唯一 key(去重與 upsert 都靠它)。
- 一份共用的連線 / 設定來源,避免各模組各抄。
步驟
- 拆連續體:把「收信 → 抽附件 → 正規化 → 入庫 → 看板」每一站到底負責什麼,先一站一站講清楚。
- 正規化:把每個人各自的 Excel 排版習慣,收斂成同一組欄位。
- upsert 入庫:用 key 去重,讓你重跑幾次都安全。
- 分離顯示與篩選:畫面要顯示的字、跟拿來比對的字,各存一份。
- 精準比對:篩選一律用 exact match,別用 contains。
停損點
- 重跑會長出重複列 → 停,先確認 key 與 upsert。
- 篩選誤命中 → 停,檢查是不是又用了子字串比對。
驗收標準
- 重匯不重複;篩選不誤命中;設定只有一份來源。
回滾:以「重跑零重複 + 篩選測試全綠」的版本為還原點。
可複製到:任何「定期收 Excel、要變成可查詢平台」的內部資料流。
決策回放(三個月後回讀用)
- 背景:供應商週期性 issue 散在信箱與 Excel,需可查詢、可維護。
- 限制:不可外流真實 issue 內容、供應商名與內部 DB 主機;沿用既有 Vault 取得 DB 機密。
- 考慮過但放棄:每次都手動整理 Excel——被「累積不起來、難維護」否決。
- 證據:upsert 去重、顯示/篩選分離、相似前綴篩選測試。
- 未解:要不要跟晶片商 B 的 tracker 併成同一個資料平台家族。
作者備註: 這種案子最容易栽在小地方——不是架構,是「篩選到底拿哪個字去比」這種細節。
Open命中Open - Pending看起來很蠢,但真的會發生。