应使用char_length()而非length()计算中文字符数,因为length()返回字节数(如'你好'返回6),而char_length()返回真实字符数('你好'返回2),前者易导致截断、误过滤及乱码等问题。

LENGTH() 返回的是字节数,不是字符数——直接用它判断“中文有几个字”会出错。
MySQL 里 LENGTH() 算的是字节数,不是你看到的“字数”
比如字段存了 '你好',在 utf8mb4 编码下占 6 字节(每个汉字 3 字节),LENGTH('你好') 就返回 6,而不是 2。这会导致:
- WHERE
LENGTH(name) > 10实际筛出的是“超 10 字节”的数据,可能把只有 4 个汉字的名字(CHAR_LENGTH=4,但LENGTH=12)也过滤掉 - 导出报表时按
LENGTH()截断,容易切在汉字中间,产生乱码 - 字段定义为
VARCHAR(10),插入'??'(3 个 emoji)时CHAR_LENGTH=3合法,但LENGTH可能达 12,触发截断或报错
该用 CHAR_LENGTH() 的时候别硬套 LENGTH()
业务上关心“用户昵称最多 20 个字符”“标题不能少于 2 个字”,这些全是字符维度需求,必须用 CHAR_LENGTH():
-
CHAR_LENGTH('你好')→2(正确) -
CHAR_LENGTH('abc ')→4(含空格,不忽略尾部) - 配合
TRIM()可准确识别空白:CHAR_LENGTH(TRIM(content)) = 0 - NULL 值仍返回 NULL,需显式兜底:
COALESCE(CHAR_LENGTH(title), 0)
跨数据库写法不统一,硬搬会报错
不同数据库对同名函数语义不同,甚至函数名都不同:
- MySQL:
LENGTH()是字节,CHAR_LENGTH()是字符 - PostgreSQL:
LENGTH()默认就是字符长度(等价于 MySQL 的CHAR_LENGTH()),要字节得用OCTET_LENGTH() - SQL Server:
LEN()是字符(自动忽略尾空格),DATALENGTH()才是字节(含空格) - SQLite:
length()(小写)是字符长度 - Oracle:
LENGTH()是字符,LENGTHB()才是字节
迁移脚本时,CHAR_LENGTH(col) 在 MySQL 和 PostgreSQL 都能跑;但把 MySQL 的 LENGTH(col) 直接抄到 SQL Server 会报 Invalid column name 'LENGTH'。
WHERE 中用 LENGTH 或 CHAR_LENGTH 会拖慢查询
对字段套函数做条件判断,比如 WHERE CHAR_LENGTH(title) > 20,MySQL 和 PostgreSQL 默认无法使用 title 上的普通索引,大概率触发全表扫描:
- MySQL 8.0+ 支持函数索引:
CREATE INDEX idx_title_len ON articles ((CHAR_LENGTH(title))); - PostgreSQL 需显式表达式索引:
CREATE INDEX idx_title_len ON articles (CHAR_LENGTH(title)); - 老版本或 SQLite 没这功能,只能加冗余字段(如
title_char_len TINYINT)并维护一致性
真正难处理的不是函数怎么写,而是字符集配置、连接层编码、ORM 默认行为三者不一致时,同一句 CHAR_LENGTH() 在开发环境和生产环境返回不同结果——这种问题不会报错,只会在上线后悄悄漏数据或错判长度。










