MySQL严格模式下缩容VARCHAR会因超长数据报错中止,必须先用LENGTH()定位超长行;允许截断则UPDATE显式处理,否则不可缩容;禁用严格模式仅限开发且需立即恢复,生产环境严禁静默丢数据。

直接结论:MySQL 在严格模式下拒绝隐式截断,ALTER TABLE ... MODIFY COLUMN 缩小字段长度时,只要存在任意一行数据超长,就会报 Data truncated for column 'xxx' at row n 并中止执行——这不是 bug,是保护机制。
查哪些行会触发截断
别猜,先定位具体超长记录。假设你要把 name VARCHAR(255) 改成 VARCHAR(50):
- 用
LENGTH()(不是CHAR_LENGTH())查字节长度,尤其当表用utf8mb4时,一个 emoji 占 4 字节:SELECT id, name, LENGTH(name) AS len FROM users WHERE LENGTH(name) > 50;
- 如果字段是
TEXT类型且怀疑超 64KB,改用OCTET_LENGTH()或直接LENGTH()(二者在 MySQL 中等价) - 注意:
WHERE条件里别写CHAR_LENGTH(name) > 50—— 它按字符数算,对多字节字符不敏感,可能漏掉真实超长的行
缩容前必须确认业务是否允许截断
允许,就主动清理;不允许,就别缩。没有中间路线:
- 若允许截断:用
UPDATE显式截断,比如UPDATE users SET name = LEFT(name, 50) WHERE LENGTH(name) > 50;
再执行ALTER - 若不允许:扩字段或改业务逻辑。盲目
MODIFY name VARCHAR(100)可能掩盖设计问题(比如本该校验手机号却存了地址) - 特别注意 JSON 字段:缩容前必须验证语法合法性,
JSON_VALID()为FALSE的行在ALTER时也会被拦住
临时关严格模式?只限开发环境且必须加防护
生产库禁止这么做。即使临时关闭,也只解决“能不能执行”,不解决“数据对不对”:
- 执行前必须加
SET sql_mode = '';,改完立刻恢复,例如:SET sql_mode = ''; ALTER TABLE users MODIFY name VARCHAR(50); SET sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';
- 关模式后,MySQL 会静默丢数据,但不会告诉你丢了哪几条——你得自己比对前后行数、校验和或抽样检查
- 线上环境若真要绕过,优先考虑
ALGORITHM=INPLACE+LOCK=NONE的在线 DDL(MySQL 5.6+),但缩容仍受严格模式约束,关模式仍是高危操作
最容易被忽略的一点:缩容操作本身不锁表,但 UPDATE 截断数据那步很可能锁行或锁表,尤其在大表上。务必在低峰期操作,并提前在从库验证整个流程是否可回滚。


















