MySQL中安全截断需先用CHAR_LENGTH()判断字符数是否超限,再用SUBSTRING()取前n位并CONCAT()拼接省略号,避免短字符串冗余“…”;注意UTF8mb4下中文占4字节,须用CHAR_LENGTH()而非LENGTH()。

MySQL中用SUBSTRING()配合CONCAT()实现安全截断
直接用SUBSTRING(str, 1, n)取前n个字符,再用CONCAT()拼接省略号,是最可控的方式。关键在于:必须先判断原字符串长度是否超过阈值,否则会在短字符串末尾多加“…”。
常见错误是写成CONCAT(SUBSTRING(name, 1, 20), '…')——哪怕name只有5个字,结果也会变成“张三……”。正确做法是嵌套CASE或IF:
SELECT
IF(LENGTH(name) > 20,
CONCAT(SUBSTRING(name, 1, 20), '…'),
name
) AS truncated_name
FROM users;
注意:LENGTH()按字节计数,中文在UTF8mb4下占4字节,若需按「字符数」截断(比如限制20个汉字),应改用CHAR_LENGTH()。
PostgreSQL里用LEFT()和||更简洁
PostgreSQL原生支持LEFT(str, n),语义比SUBSTRING清晰,且自动处理越界(LEFT('abc', 10)返回'abc',不报错)。拼接用||比CONCAT()更轻量。
但同样要防冗余省略号,推荐写法:
SELECT
CASE
WHEN CHAR_LENGTH(title) > 30
THEN LEFT(title, 30) || '…'
ELSE title
END AS truncated_title
FROM articles;
这里用CHAR_LENGTH()是必须的——PostgreSQL的LENGTH()默认也按字节,对中文不友好;而LEFT()内部按字符切分,与CHAR_LENGTH()行为一致,避免混用导致逻辑错位。
SQL Server中LEFT() + IIF()一行搞定,但要注意NULL传播
SQL Server的LEFT(str, n)同样安全越界,IIF()语法比CASE紧凑。但极易踩的坑是:如果原始字段为NULL,LEN(NULL)返回NULL,整个IIF条件判为未知,结果仍是NULL,而非预期的NULL或空字符串。
稳妥写法必须显式处理NULL:
SELECT
IIF(
LEN(ISNULL(full_name, '')) > 25,
LEFT(full_name, 25) + N'…',
full_name
) AS truncated_name
FROM customers;
注意两点:ISNULL(full_name, '')把NULL转为空字符串再算长度;省略号前加N前缀(N'…')确保Unicode正确,否则在某些排序规则下可能显示为方块或乱码。
通用陷阱:前端二次截断时省略号重复、宽度计算失准
后端SQL截断只是第一道防线。如果前端还用CSS(如text-overflow: ellipsis)或JS再次截断,可能造成“文本……”这种双省略号;更隐蔽的问题是:数据库里存的是全量字符串,但前端展示用了等宽字体+固定字符数限制,而中英文、标点、Emoji宽度不同,纯靠CHAR_LENGTH()截20个字符,视觉上仍可能溢出容器。
真正健壮的做法是:
- SQL层只做「最大长度兜底」,比如保证
CHAR_LENGTH() ,不强求视觉对齐 - 前端用
ch或em单位控制容器宽度,配合text-overflow交由浏览器渲染裁剪 - 若必须服务端精确控制视觉长度(如生成报表PDF),得引入字符宽度映射表,SQL无法胜任,应移交应用层
数据库不是排版引擎,它的责任是不爆长度、不丢数据、不加冗余符号——做到这三点,就已经踩对了大多数人的雷区。

















