必须两端同步转换(如WHERE UPPER(name) = UPPER('alice')),否则因大小写平面不一致导致匹配失效且索引无法下推;单转一边无效,COLLATE/ILIKE跨库兼容性差,函数索引是性能关键。

必须两端同时转换,否则匹配失效且索引无法下推。 单靠 UPPER() 或 LOWER() 本身不提供“忽略大小写”的语义,它们只是字符串变换工具;真正起作用的是你是否让字段值和查询值落在同一大小写平面进行比对。
为什么只转一边会漏数据
常见错误写法:WHERE UPPER(name) = 'alice' 或 WHERE name = UPPER('alice')。前者把字段全转成大写(如 'Alice' → 'ALICE'),却拿小写字符串 'alice' 去比,永远不等;后者用原始字段(可能是 'alice'、'Alice' 或 'ALICE')直接跟 'ALICE' 比,只有全大写的记录才命中。
正确做法必须同步转换两边:
WHERE UPPER(name) = UPPER('alice')WHERE LOWER(name) = LOWER('alice')- 若涉及拼接,记得用
COALESCE()防NULL中断:UPPER(CONCAT(COALESCE(first_name,''), COALESCE(last_name,'')))
不同数据库的性能关键:函数索引不是可选项
写成 UPPER(name) = UPPER(?) 后,普通索引完全失效——因为索引是按原始值构建的,而你在查转换后的值。
必须为转换表达式单独建函数索引,否则百万级表可能从毫秒退化到秒级响应:
- PostgreSQL:
CREATE INDEX idx_users_upper_name ON users (UPPER(name)); - MySQL 8.0+:
CREATE INDEX idx_users_upper_name ON users ((UPPER(name)));(注意双括号) - SQL Server:
CREATE INDEX idx_users_upper_name ON users (UPPER(name)); - SQLite:
CREATE INDEX idx_users_upper_name ON users (UPPER(name));
漏建这个索引,开发阶段几乎看不出问题,上线后查得越久越慢,而且优化器不会警告你。
COLLATE 和 ILIKE 是替代方案,但不通用
COLLATE 在各库行为差异极大:
- MySQL 支持,但若字段原 collation 是
_bin,加COLLATE utf8mb4_0900_as_ci可能跳过索引 - PostgreSQL 不允许
WHERE name COLLATE ... = 'val',语法报错;只能用ILIKE(仅模糊)或坚持LOWER() - SQL Server 支持,但 JOIN 时若两表 collation 不一致,直接报
Cannot resolve collation conflict - SQLite 完全不支持查询中加
COLLATE,只认建表时声明的NOCASE
ILIKE 是 PostgreSQL 专属,语义清晰、支持通配符,但无法跨库移植;如果项目要兼容多种数据库,LOWER(col) = LOWER(val) 加函数索引仍是唯一可靠路径。
真正难的不是写出能跑的 SQL,而是确认当前字段的 collation 实际生效逻辑、判断是否已建对应函数索引、以及验证执行计划里是否真用了它——这些步骤缺一不可,且容易在本地小数据量下被完全忽略。

















