先说一个最简单的场景:当你在数据库里搜索 'john' 时,结果里大概率看不到 'John' 或 'JOHN'。这不是系统针对你,而是因为大小写敏感是数据库的默认行为。

要想绕开这个限制,最常见的做法就是用 UPPER 或 LOWER 函数把两边统一成同一种大小写状态。但这里面有很多门道,稍不注意就会踩坑。
UPPER/LOWER 函数在 WHERE 子句中必须成对使用
关键点在于:直接写 WHERE name = 'john' 永远不会命中 'John' 或 'JOHN',这是既定事实。要打破这个限制,你必须让等式两边的字母大小写保持一致——要么全大写,要么全小写。
一个很常见的错误是只转一边。比如 WHERE UPPER(name) = 'john'(右边没转),或者 WHERE name = UPPER('john')(左边没转)。这两种写法的共同问题是:一边是转换后的值,一边是原始值,二者之间的大小写形态并不统一,比较自然会失败。
- 正确的写法是:
WHERE UPPER(name) = UPPER('john')或WHERE LOWER(name) = LOWER('john')。 - 实际项目里通常更推荐用
LOWER。原因在于,部分数据库(比如 PostgreSQL)对 Unicode 大写字符的支持稳定性不如小写;而且UPPER在某些土耳其语环境下可能会因为语言敏感字符而出问题。 - 如果搜索词来自用户输入,建议在应用层先做一次
LOWER或UPPER预处理。这样就不再需要在 SQL 里重复调用函数了,能提升代码的可读性,也算是一种性能微优化。
索引失效问题比想象中更常见
一旦你在列上套用了 UPPER() 或 LOWER(),大多数数据库(包括 MySQL、PostgreSQL、SQL Server)就会跳过普通的 B-tree 索引,直接执行全表扫描。这不是“慢一点”的事,数据量稍微上来,响应时间就明显卡顿。
要解决这个问题,不是不能使用函数,而是需要提前固化大小写的一致性:
- 建表时增加一个生成列。比如
name_lower TEXT GENERATED ALWAYS AS (LOWER(name)) STORED(PostgreSQL 和 MySQL 5.7+ 都支持),然后对这个生成列单独建索引。 - 或者,业务写入时就直接把
LOWER(name)的结果存入一个专门字段(比如name_ci),查询时直接走这个字段和对应索引。 - 如果用的是 MySQL 8.0+,还能使用函数索引:
CREATE INDEX idx_name_lower ON users ((LOWER(name)))。务必注意括号的写法:它不是把LOWER(name)当作字段名,而是当作一个表达式。
LIKE 模糊搜索时大小写处理不能省略
至于模糊搜索,WHERE LOWER(name) LIKE LOWER('%john%') 是安全的写法。但写成 WHERE name LIKE '%john%' 的话,在多数数据库里依然遵循大小写敏感的规则,除非你显式启用了 case-insensitive collation。
这里有三个容易忽略的陷阱:
- PostgreSQL 默认区分大小写,
ILIKE是一个特例,但它只属于 PostgreSQL 。其他数据库没有这个等价语法。 - MySQL 如果用
utf8mb4_0900_as_cs这类大小写敏感的排序规则,LIKE也同样是区分大小写的。 - 参数化查询时,如果传入的是原始关键词,仍然需要在 SQL 中显式包裹
LOWER()。不能指望驱动或 ORM 来自动帮你处理这个转化步骤。
不同数据库对空值和特殊字符的处理差异
在处理 NULL 时,UPPER(NULL) 和 LOWER(NULL) 在所有主流数据库里都返回 NULL,这本身是合理的。但容易被忽略的是:非 ASCII 字符(比如中文、德语 ß、希腊字母)的大小写转换行为,在不同数据库之间可能有微妙差异。
举个例子:
- MySQL 的
UPPER('straße')会返回'STRASSE'(ß → SS),而 PostgreSQL 的返回结果可能一样,也可能不一样,取决于 locale 配置。结果看似一致,但底层原理不同。 - SQLite 并不内置对 Unicode 的
UPPER/LOWER支持。遇到中文直接原样返回,所以不能用来做中文关键词的归一化。 - 如果关键词里包含空格或标点(比如
'John D.'),LOWER只影响字母,不会改变结构。所以预处理时是否需要 trim,是否要替换多余空格,需要根据业务逻辑来决定。
这里有一个更麻烦的现实:真正棘手的不是函数本身,而是你必须清楚当前数据库版本、字符集、排序规则三者共同作用下,这些函数到底会怎么做。光看文档往往不够,最好在目标环境中拿几个边界词汇实测一遍,才能确定最终结果是否如预期。这大概就是所谓的“日常开发里99%的情况不会出问题,但那剩下的1%才是最值得留意的”。
