sql标准like不支持[a-z]类字符范围语法,该写法会被当作字面量匹配;实现范围匹配应使用各数据库的正则功能(如mysql的regexp_like、postgresql的~操作符)或替代方案。

LIKE 中的字符集范围写法依赖 COLLATION,不是正则
SQL 的 LIKE 本身不支持类似正则里的 [a-z] 或 [0-9] 字符集范围语法(MySQL 8.0+ 的 REGEXP_LIKE 才支持)。你看到的 LIKE 'a[b-d]e' 这种写法在标准 SQL 和绝大多数数据库里**根本无效**,会直接当作字面量匹配——也就是查字符串 a[b-d]e,而非“a + b/c/d 中任一 + e”。
真正能实现字符范围匹配的,是借助排序规则(COLLATION)隐式影响比较行为,或用数据库特定扩展。实操中更可靠的方式是:
- MySQL:用
REGEXP/REGEXP_LIKE(8.0+),例如WHERE name REGEXP '^a[b-d]e$' - PostgreSQL:用
~操作符或REGEXP_MATCHES(),例如WHERE name ~ '^a[b-d]e$' - SQL Server:不支持原生字符类,需用
LIKE配合多个OR,如name LIKE 'abe' OR name LIKE 'ace' OR name LIKE 'ade';或启用 CLR 正则函数 - SQLite:无内置正则,需加载扩展(如
regexp函数)或改用应用层过滤
MySQL 中误用 [a-z] 导致全表扫描的典型陷阱
有人在 MySQL 中写 WHERE col LIKE 'A[a-z]%' COLLATE utf8mb4_0900_as_cs,以为能匹配 “Abc”、“Ax123”,结果要么没结果,要么慢得离谱。问题出在两处:
-
LIKE的方括号[...]不被解析为字符类,而是普通字符——该语句实际在找以A[a-z]%开头的字符串(即字面包含[、a、-、z、]) - 即使你本意是想靠 collation 实现大小写/范围敏感匹配,
utf8mb4_0900_as_cs等二进制排序规则只控制“相等性”,不改变LIKE的通配符逻辑 - 一旦表达式无法使用索引(比如开头是通配符或含非法模式),就会触发全表扫描
正确做法是换用 REGEXP 并确保字段有前缀索引(仅对左锚定有效):WHERE col REGEXP '^A[a-z]',且 col 建有 INDEX(col(5)) 可辅助前缀过滤。
PostgreSQL 中用 POSIX 字符类替代 [a-z] 更安全
PostgreSQL 的 ~ 支持标准 POSIX 字符类,比手写 [a-z] 更健壮,尤其涉及多语言时:
-
WHERE name ~ '^A[:lower:]'匹配 “Abc”、“Axyz”,但不匹配 “A123” 或 “ABC” -
[:digit:]、[:alnum:]、[:punct:]等都可用,且受当前LC_CTYPE影响,比硬编码 ASCII 范围更可靠 - 注意:默认是区分大小写的;若要忽略大小写,用
~*,例如name ~* '^a[:lower:]' - 性能上,纯正则无法走 B-tree 索引,但可配合
pg_trgm扩展建立 trigram 索引加速模糊匹配
SQL Server 中模拟字符范围:避免 OR 爆炸的折中方案
当必须用 LIKE 且范围较大(比如匹配首字母为 A–Z 的所有值),写 26 个 OR 显然不可维护。这时可考虑:
- 用
LEFT(col, 1) BETWEEN 'A' AND 'Z'替代,前提是确定字符集是单字节且无重音符号(如 Latin1_General_CI_AS) - 结合
ASCII()函数:WHERE ASCII(LEFT(col, 1)) BETWEEN 65 AND 90,兼容性更好,但无法利用索引中的函数表达式(除非建计算列索引) - 如果字段已建索引,优先用
col >= 'A' AND col —— 利用 ASCII 序,“[” 是 “Z” 后第一个可打印字符,该写法能走索引范围扫描
字符集范围匹配的本质不是语法糖,而是数据分布、排序规则、索引能力和执行引擎的共同约束。别指望一个 LIKE 表达式包打天下,先看执行计划,再选工具链。










