Day 12 處理的是單筆設備事件:讀數能否解析、缺少識別資訊時怎麼辦、重送資料該留哪一筆。到了下一步,我想從清洗後的資料產出各設備的每日統計,供人觀察讀數變化或異常事件。這時我原本會先看彙總結果是否合理;只要數字看起來對,就覺得工作完成了。
但「看起來合理」沒有回答兩個問題:今天的轉換規則是否真的按預期執行?明天新資料進來時,是否還有人會重做同樣的檢查?這次 SQL 實作讓我從建立結果表,往前多走一步:為結果寫出可以反覆執行的驗證規則。
在 BigQuery 實作中,我先從乾淨資料建立彙總表,再核對明細與彙總結果。這種對帳很有價值:如果明細經過篩選,彙總卻讀到未清洗的來源,結果就可能對不上;如果資料重複計入,也可能從數量或總和發現異常。
不過,我現在不會把「總數相同」寫成「所有清洗規則正確」的證明。兩筆讀數分別高估與低估,平均值可能剛好不變;兩台設備的事件歸屬錯置,全部設備的事件總數也可能一樣。對設備資料,我除了核對總量,還會分設備、日期和事件類型檢查,並確認彙總使用的是已約定的時間與單位。
對帳回答的是「這些結果是否一致」,還需要其他規則回答「它們是否符合定義」。
這次實作使用 SQL 檢查清洗後的資料:先寫出應成立的條件,再找出違反條件的紀錄。延伸到設備事件,我會檢查供分析的資料是否仍有缺少設備識別資訊的事件、是否混入尚未換成約定單位的讀數,以及同一事件是否被重複計入。這些是擬定中的驗收規則,不是我已對設備資料跑出的檢查結果。
Google Cloud 的 Dataform 文件將這類檢查稱為 assertion(斷言)。手寫的 SQLX 斷言查詢必須回傳零列;只要回傳違規紀錄,就視為失敗。它也讓我在失敗時直接看到問題資料,而不只得到一個模糊的紅燈。
讀實作 SQL 時,我發現一個容易混淆的地方:SELECT COUNT(*) 即使算出違規數為零,查詢仍會回傳一列,裡面的數值是零。這和「回傳零列違規資料」不同。如果要把查詢搬進 Dataform 作為手寫斷言,就不能只看畫面上是不是零;必須確認查詢的輸出形式符合 Dataform 的判準。
另一個陷阱是 NULL。例如只寫「讀數超出允許範圍」,尚未解析成功而成為 NULL 的值,未必會被這個比較條件抓出來。我應另外檢查「讀數不得為 NULL」,或在同一條違規條件中明確納入空值。
因此品質檢查不能只在正常資料上看起來能運作。我還要刻意用缺值、重複及邊界情況確認:這條斷言真的會在該失敗的時候失敗。
手動執行時,我知道要先產生乾淨資料,再做彙總,最後檢查結果。但如果資料每天更新,不能只靠我記得依序按下執行。Dataform 官方文件說明,可以宣告資料表之間的依賴,工作流程會依依賴順序執行;也能設定工作流程的排程。
順序之外,還要決定品質失敗是否阻擋下游更新。下游依賴上游資料表,不等於自動等待上游的斷言通過。 Dataform 提供將依賴對象的斷言納入依賴關係的設定;正式建立流程時,我會明確設定並驗證這個行為。否則,即使檢查最後亮紅燈,報表也可能已用到不符合規則的資料。
我可以在今天截下「沒有違規資料」的結果,但它只能證明這次執行的狀態。設備更新韌體、來源調整欄位或轉換規則改版之後,同一條管線可能開始產生不同結果。若每次檢查都留下執行時間、規則版本與違規情形,我才能看見品質是在改善,還是逐漸退步。
Google Cloud 現行的 Knowledge Catalog(原 Dataplex Universal Catalog)支援資料品質掃描、排程與結果管理,也可用非空值、唯一性和自訂 SQL 等規則檢查資料。 對我而言,這不是要把每一條 SQL 都立刻搬到另一個產品;它提示了下一個階段:把一次性的驗收,變成有執行紀錄、能持續觀察的品質管理。至於可接受的缺值率或異常門檻,仍須由資料使用者與來源負責人共同確認。
假設有人質疑某台設備的每日平均讀數,我需要一路查回:彙總用了哪些事件、哪些事件曾被隔離、讀數有沒有換算單位、來源最初送來什麼。Google Cloud 在 2025 年介紹 BigQuery 的欄位層級 lineage(資料血緣)時,強調可以追查欄位從來源到下游的流轉與影響。這是對本次 SQL 驗收的進一步延伸,不能取代我自己對規則正確性的檢查。
我現在會把交付一張彙總表,理解為交付一個可以被追問的結果:它有明確定義,更新時會接受檢查,出錯時能找到違規資料與來源。這個要求不只適用於 BigQuery。無論換哪個雲端平台,可信的資料都需要讓人知道它如何產生,以及下一次更新後如何再次證明它可信。