具代表性的面試主題

資料面試:如何把報表表達式遷移為 PostgreSQL 18 虛擬生成欄?

資料困難
Offer.cc 編輯團隊發佈 更新

題幹

報表查詢重複計算 `lower(trim(country_code))`。升級 PostgreSQL 18 後,你會如何遷移為 VIRTUAL 生成欄,並證明結果、效能與權限行為沒有回歸?

題幹與適用情境

這道題不比較所有生成欄方案,只討論把重複的列內表達式遷移為 PostgreSQL 18 虛擬生成欄。目標是統一計算語意並避免儲存生成欄造成的表重寫,同時控制讀取 CPU、函式限制與權限變化。

面試官考察重點

  • 是否知道 PostgreSQL 18 新增虛擬生成欄並設為預設類型。
  • 是否先證明表達式只參考目前列、使用不可變的內建函式與類型。
  • 是否用雙讀對帳和真實查詢計畫驗證遷移。
  • 是否理解虛擬欄在讀取時計算、不儲存,且不能作為分割區鍵。

回答前需要釐清的問題

確認表達式是否只含內建函式與類型、哪些查詢會讀取或篩選此欄、目前是否有表達式索引,以及應用能否暫時同時讀取舊表達式與新欄。也要核對角色權限,因為生成欄與基礎欄可分別授權;若要用生成欄隔離基礎欄,必須逐一驗證表達式中的函式、運算子和型別轉換是否符合 LEAKPROOF 條件。

30 秒回答架構

我會先審查表達式符合 PostgreSQL 18 虛擬欄限制,再新增明確標示 VIRTUAL 的欄位。遷移期影子讀取舊表達式和新欄,覆蓋空值、Unicode、異常輸入和歷史分割區;同時比較查詢 CPU、尾延遲與執行計畫。通過後切換讀取,保留回退舊表達式的路徑,最後才清理重複 SQL。

分步深入解析

sql
ALTER TABLE report_events
ADD COLUMN normalized_country text
GENERATED ALWAYS AS (lower(trim(country_code))) VIRTUAL;

虛擬欄在讀取時計算且不佔列儲存。表達式只能參考目前列,不可含子查詢或其他生成欄,函式必須不可變;虛擬欄也不能依賴自訂函式或類型。明確寫出 VIRTUAL 可避免團隊誤讀 PostgreSQL 18 的新預設值。

先掃描歷史資料,比較 normalized_country IS NOT DISTINCT FROM lower(trim(country_code)),覆蓋 NULL、空白、大小寫與非 ASCII 輸入。再以 EXPLAIN (ANALYZE, BUFFERS) 驗證高頻查詢,確認讀時計算沒有造成不可接受的 CPU;需要索引時另行驗證支援與寫入成本。

發布依序新增欄位、影子雙讀並以舊結果為準、差異為零且效能通過後切換。回滾只需恢復舊查詢。最後用實際角色驗證基礎欄與虛擬欄的欄級授權;若任一函式、運算子或轉換無法證明為 LEAKPROOF,就不能把虛擬欄權限視為對基礎欄的完整安全隔離。

高品質示範回答

我會把它當成查詢契約遷移。確認表達式只使用目前列的內建不可變函式後新增虛擬欄。它免除物理回填,卻把成本留在每次讀取,因此必須用生產查詢形狀驗證 CPU 和尾延遲。

應用先影子比較新舊結果,按輸入類型統計差異;一致後才切換。權限、檢視與用戶端欄位映射一起驗收。若效能或語意回歸,基礎欄仍在,可立即恢復舊表達式。

常見錯誤

  • 省略 VIRTUAL,讓 DDL 意圖依賴版本預設值。
  • 使用可變函式、子查詢或自訂類型後才發現不支援。
  • 只測一般英文值,漏掉 NULL 與 Unicode。
  • 把「不佔列儲存」誤解成沒有查詢 CPU。
  • 切換時刪掉舊表達式,失去快速回滾。

追問與回答

為什麼不選 STORED?

此任務要統一便宜的讀時表達式並避免物理回填。若大量掃描或排序的壓測結果不佳,才重新評估儲存生成欄。

虛擬欄能作為分割區鍵嗎?

不能。PostgreSQL 18 的生成欄不能成為分割區鍵;需要時應使用由寫入路徑維護的一般欄位。

如何驗證權限沒有擴大?

用實際應用角色分別查詢基礎欄與生成欄,核對欄級授權和表達式函式的執行權限。若生成欄承擔隱藏基礎欄的安全邊界,還要檢查每個函式,以及運算子或型別轉換背後的函式是否標記為 LEAKPROOF;PostgreSQL 不會替應用強制這項條件。只要有一條表達式路徑無法證明,就不能把生成欄授權當作完整隔離。

何時結束雙讀?

完整業務週期、歷史分割區與峰值負載都通過,差異持續為零,才停止雙讀並清理重複 SQL。

公開來源

同類題目