题干与适用场景
系统要按用户输入查找用户名或邮箱,要求遵循 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,并在同环境执行迁移与回归测试。