資料工程面試:PostgreSQL 18 的 NOT ENFORCED 約束能解決什麼問題?
題幹與適用場景
一張歷史訂單表 orders 中有少量找不到客戶的 customer_id。團隊想在 PostgreSQL 18 先把外鍵寫入 schema,明確目標關係,同時不讓舊資料修復被一次遷移阻塞。請判斷 NOT ENFORCED 是否合適,說明它和普通外鍵的差異、如何發現違規列、如何分階段切換,以及為什麼不能把它當成資料已合規的證明。
面試官考察點
- 能否區分「宣告業務意圖」和「由資料庫拒絕非法寫入」。
- 能否說明
NOT ENFORCED不會在寫入時檢查約束,因此不能提供執行期完整性保證。 - 能否設計歷史資料掃描、增量監控、修復批次與最終切換門檻。
- 能否辨識 dump、還原、跨服務寫入、回滾和應用相容風險。
- 能否解釋為什麼不能借用未確認的約束改變查詢或最佳化假設。
回答前需要釐清的問題
- 約束是外鍵還是 CHECK?違規資料的規模、增長速度和修復負責人是誰?
- 寫入路徑只有 PostgreSQL,還是也有 ETL、批次或繞過應用程式的腳本?
- 舊客戶端能否處理新增約束名稱、遷移鎖和切換失敗?
- 最終切換必須零違規,還是允許依租戶分批完成?
- 需要保留哪些證據,才能證明每次掃描涵蓋完整資料範圍?
30 秒回答框架
「NOT ENFORCED 適合先把 CHECK 或外鍵的意圖寫進 schema,同時允許歷史髒資料存在;它不會由資料庫檢查新寫入,因此不是完整性保證。我會先建立全量違規基線,再對新寫入做增量檢查和告警,修復或隔離歷史列,最後在零違規和回滾演練通過後切成 enforced。期間不能讓客戶端或最佳化器把它當成已驗證的事實。」
分步驟深入解答
先釐清語義與風險
PostgreSQL 18 允許 CHECK 與 foreign key 指定 NOT ENFORCED。資料庫保存約束宣告,但不會像 enforced 約束那樣檢查寫入。約束元資料可用於文件、治理與遷移協調;它不能取代應用程式、ETL 或獨立資料品質檢查。
建立全量與增量證據
對外鍵做反連接掃描,找出沒有父表記錄的子表列;對 CHECK 執行同一謂詞的反向查詢。記錄掃描時間、快照或水位、分區範圍和結果摘要。之後在 CDC、寫入作業或品質任務中檢查新增和更新列,避免只做一次歷史掃描。
SELECT o.order_id, o.customer_id
FROM orders AS o
LEFT JOIN customers AS c ON c.customer_id = o.customer_id
WHERE o.customer_id IS NOT NULL
AND c.customer_id IS NULL;設計修復與切換門檻
把孤兒訂單分成可自動補齊、需要業務確認和必須隔離三類。為修復作業設定冪等鍵、批次上限和停止條件。切換前要求全量掃描為零、增量窗口沒有新增違規、備份還原演練通過、所有寫入路徑納入監控。接著在低峰執行 enforced 遷移,觀察鎖等待和錯誤率。
處理還原與跨環境差異
遷移失敗時保留 NOT ENFORCED 宣告和已修復記錄,回滾應用程式發布與品質任務,不要假裝約束已 enforced。測試 dump/restore、唯讀副本、災備叢集和舊版本客戶端;確保目標環境理解 conenforced 元資料,不能只依賴 ORM 是否顯示欄位。
高品質示範回答
「我會把 NOT ENFORCED 當成遷移期契約,不當成資料庫護欄。先對訂單和客戶做完整反連接,記錄分區範圍和水位,再讓 CDC 任務檢查新寫入。孤兒列依自動修復、人工確認和隔離分類,所有修復可冪等重跑。只有全量與增量都零違規、備份還原通過、寫入路徑完整納管,才在低峰把外鍵切成 enforced,並監控鎖等待。任何階段都不會讓客戶端或查詢最佳化假設這條約束已被驗證。」
常見錯誤
- 錯誤表現: 看到 schema 有外鍵就認為資料一致 → 失敗原因:
NOT ENFORCED不檢查寫入 → 修正方法: 用全量與增量品質證據證明狀態。 - 錯誤表現: 只掃描一次歷史表 → 失敗原因: 新的繞過路徑仍會產生違規 → 修正方法: 覆蓋 CDC、ETL、腳本和應用程式寫入。
- 錯誤表現: 直接把約束改成 enforced → 失敗原因: 遷移可能因髒資料或鎖失敗 → 修正方法: 先修復、演練、設定門檻,再在低峰切換。
- 錯誤表現: 把未 enforced 約束當成最佳化提示 → 失敗原因: 宣告沒有提供完整性證明 → 修正方法: 只依據已驗證統計和資料庫實際支援的規則。
追問及應對
CHECK 約束能引用另一張表完成跨表檢查嗎?
不要依賴這種做法。行級 CHECK 不保證其他列變更後的全域狀態,dump/restore 順序也可能暴露問題;跨表關係應優先使用外鍵或獨立品質任務。
如果違規數量永遠降不到零怎麼辦?
保留 NOT ENFORCED,把違規列納入隔離或業務例外清單,並設定明確的增長上限、負責人與到期日。若約束只是文件而不是近期可執行契約,應重新評估是否需要寫入 schema。
能靠應用程式校驗取代 enforced 外鍵嗎?
只能作為過渡或補充。多寫入者、並發和繞過應用程式的腳本會讓應用校驗失效;最終完整性邊界仍應由資料庫約束或可驗證資料管道承擔。
如何驗證災備庫沒有悄悄偏離?
在主庫和還原副本執行同一品質查詢,比較掃描水位、違規數量和結果摘要,並把還原演練納入切換門檻。
參考資料
- PostgreSQL Documentation 18:Constraints
- PostgreSQL Documentation 18:CREATE TABLE
- PostgreSQL Documentation 18:Release Notes
- Greg Low:SQL Interview: 64 Disabling and reenabling constraints
- MockIF:SQL Interview Questions 2026
面試作答要點
先說清 NOT ENFORCED 只宣告約束、不提供寫入檢查,再給出全量基線、增量監控、修復分類、切換門檻和還原證據。
一句話總結
NOT ENFORCED 是遷移期的可見契約,資料工程師仍須用獨立證據證明資料合規,才能承擔 enforced 的執行期護欄。