題干與適用場景
系統要依使用者輸入查找使用者名稱或電子郵件,要求遵循 Unicode Default Caseless Matching。請說明如何評估 PostgreSQL 18 的 casefold()、選擇 collation、建立索引和遷移既有資料。不要只給出一個函式呼叫。
面試官考察點
- 是否理解 case folding 與簡單轉小寫不同,某些字元會折疊成多個字元。
- 是否核對 UTF-8、collation provider 與資料庫環境限制。
- 是否能把表達式索引、唯一性、正規化和遷移順序說清楚。
- 是否設計衝突檢測、回滾、效能驗證和使用者可見的規則說明。
回答前需要釐清的問題
- 業務要求的是 Unicode 預設無大小寫匹配,還是某個語言環境的排序規則?
- 結果用於搜尋、登入匹配,還是必須保證正規化後的全域唯一?
- 既有資料是否含有 ß、希臘字母或組合字元?是否允許改寫展示值?
- 資料庫編碼、collation provider、版本和線上索引變更窗口是什麼?
30 秒回答框架
我會先確認匹配規則和唯一性語義,再驗證資料庫使用 UTF-8 與支援 case folding 的 collation。casefold() 負責產生比較鍵,不應直接替換展示值;某些字元會改變長度,且 libc provider 的行為可能退化為 lower()。遷移時先離線生成比較鍵並找衝突,再建立表達式索引或儲存欄位的唯一約束,分批切換讀寫並監控查詢計畫。上線前用多語言樣本驗證結果、長度、索引命中和回滾路徑。
分步驟深入解答
1. 先定義匹配與展示邊界
把使用者看到的原文和用於比較的鍵分開保存。確認是否還需要 Unicode normalization、移除空格或電子郵件特定規則;casefold 只解決大小寫折疊,不會自動完成所有業務清洗。
2. 核對編碼與 collation
官方文件要求伺服器編碼為 UTF-8。case folding 依賴 collation;Unicode collation 可以把 ß 折疊為 ss,而 libc provider 不支援真正的 case folding 時會與 lower() 相同。部署前應在目標環境執行代表性樣本。
3. 設計索引與唯一性
搜尋可使用 casefold(column) 的表達式索引,唯一性則要明確比較鍵是否持久化以及如何處理歷史衝突。不要假設函式結果長度不變,也不要讓展示欄位承擔唯一性判斷。
4. 安全遷移與驗證
先掃描並記錄折疊後相同的既有記錄,制定合併或人工處理規則,再分批回填比較鍵和建立約束。用 EXPLAIN、真實多語言樣本、並發寫入和失敗重試驗證;發現衝突時暫停切換並保留回滾開關。
高品質示範回答
我會把 casefold() 當作比較鍵生成規則,而不是展示值轉換。先確認業務要的是 Unicode Default Caseless Matching,資料庫編碼為 UTF-8,並在目標 collation 下驗證 ß 等字元的折疊結果;同時檢查 provider,因為 libc 不支援真正的 case folding 時會退化為 lower()。接著掃描既有資料找折疊衝突,決定儲存比較鍵或使用表達式索引,再建立相應唯一約束。遷移分批回填並雙寫,使用查詢計畫、並發衝突、多語言樣本和回滾開關驗證,向使用者明確原文展示與匹配規則的差異。
常見錯誤
- 把
casefold()當成簡單lower()的別名。 - 忽略 UTF-8、collation 和 provider 的部署差異。
- 假設折疊結果長度不變,直接截斷或固定分配欄位。
- 未掃描歷史衝突就建立唯一約束。
- 用比較鍵覆蓋使用者展示值,導致資料不可逆。
- 只做功能測試,不看表達式索引和真實多語言查詢計畫。
追問及應對
casefold 能取代 normalization 嗎?
不能。它解決大小寫折疊;組合字元、相容字元和業務清洗仍需另外定義 normalization 規則,並透過樣本測試確認順序。
為什麼 ß 是好測試樣本?
在 PGUNICODEFAST 等 collation 中,ß 可能折疊為 ss,結果長度改變,能同時暴露匹配語義、欄位長度和唯一性設計問題。
libc provider 會帶來什麼風險?
官方文件說明 libc 不支援 case folding 時,casefold 會與 lower 相同。生產發布前應鎖定 collation/provider,並在同環境執行遷移與回歸測試。