MIN()和MAX()在字符字段上按字典序比较:MAX('v1.2','v1.10','v2.1')得'v2.1'正确,但MIN()返回'v1.10'而非'v1.2',因'1'的ASCII码49小于'2',第二位即判定大小。

MIN() 和 MAX() 在字符字段上“表现异常”,根本不是函数出问题,而是你在拿数值逻辑要求字符串比较——它严格按字典序逐字符比 Unicode 码点,不理解业务语义。
字符串比较按字典序,不是按数值大小
比如字段存的是版本号 'v1.2'、'v1.10'、'v2.1',执行 MAX(version) 返回 'v2.1'(正确),但 MIN(version) 会返回 'v1.10',因为 '1' 的 ASCII 码(49)小于 '2'(50),第二位就决定了 'v1.10' 。这不是 bug,是字符串比较的必然行为。
- 首字符不同 → 直接决定大小,后续字符不参与比较
- 首字符相同 → 比第二位,依此类推,直到某位不同或一串结束
-
''(空字符串)在多数 collation 下字典序最小,可能被误选为MIN()结果 - 中文按 Unicode 码点比,
'李'(U+674E)'张'(U+5F20),所以MIN('李四','张三')是'李四'
校对规则(COLLATE)直接控制比较结果
同一个字段,MIN(name) 在不同 collation 下可能返回完全不同的值。例如:
-
utf8mb4_bin:逐字节比较,'A'≠'a',且大小写敏感 -
utf8mb4_0900_as_cs:区分大小写 + 按 Unicode 9.0 规则排中文(拼音首字母) -
utf8mb4_general_ci:不区分大小写,但排序不稳定,已弃用
执行 SHOW CREATE TABLE your_table; 查看字段实际 COLLATE;临时验证可用 SELECT MIN(name COLLATE utf8mb4_0900_as_cs) FROM t;;永久修改需 ALTER TABLE t MODIFY name VARCHAR(50) COLLATE utf8mb4_0900_as_cs;。注意:改 collation 可能影响索引有效性与 JOIN 行为。
数字字符串必须显式转数值,不能靠 collation 修复
字段类型是 VARCHAR 但存纯数字(如 '1'、'10'、'2'),MAX(code) 必然返回 '2'。此时改 collation 无效,必须运行时转换:
- MySQL 推荐:
MAX(CAST(code AS UNSIGNED))或MAX(code + 0)(但code + 0遇NULL得 0,遇非数字如'abc'也得 0,有风险) - 更安全写法:
MAX(CAST(NULLIF(TRIM(code), '') AS UNSIGNED)),先去空格再判空 - 前缀编号如
'ID001':MAX(CAST(SUBSTRING(code, 3) AS UNSIGNED)) - 根本解法:把结构化数据拆列存储,如版本号存为
major、minor、patch整数字段
NULL 和空字符串的处理逻辑常被混淆
MIN() 和 MAX() 自动跳过 NULL,但把 '' 当作合法值参与比较——而 '' 字典序通常最小,容易污染结果。
-
WHERE name = ''不匹配NULL,但MIN(name)会忽略NULL却保留'' - 安全排除方式:
SELECT MIN(NULLIF(TRIM(name), '')) FROM t;(TRIM去空格,NULLIF把空串转NULL,MIN自动跳过) - 若整列只剩
''和NULL,结果就是NULL,需用COALESCE(MIN(...), '[unknown]')显式兜底 - 加
COUNT(name)辅助判断:若COUNT(name) = 0,说明该组无有效非空字符串值,不是“没数据”,而是数据缺失
真正容易被忽略的是:你指望数据库“猜”出你要的是数值大小,但它只认字节和 collation;一次 MAX() 调用背后,可能是索引失效、全表扫描,或是报表里一个永远填不满的空白单元格。











