结论:data too long 错误主因是字段字节数与连接编码不一致;需用 length() 查真实字节、清理超长数据、谨慎选 varchar/text,并确保四层编码统一。

直接说结论:别急着改 VARCHAR 长度,先确认「字段定义字节数」和「当前连接编码」是否一致——90% 的 Data too long for column 错误根源就在这两者错位。
查字段真实字节容量,不是字符数
MySQL 校验长度看的是字节数,不是你肉眼数的汉字个数。比如 VARCHAR(255) 在 utf8mb4 下最多存 1020 字节(255 × 4),但一个生僻字或 emoji 可能占 4 字节,8 个汉字就可能超 32 字节——而你只盯着“255”以为很宽裕。
- 用
DESCRIBE table_name;看字段类型和长度 - 用
SHOW CREATE TABLE table_name;检查该列是否被显式设为latin1或旧版utf8(三字节)——哪怕表默认是utf8mb4,单列也可能被覆盖 - 执行
SELECT MAX(LENGTH(column_name)) FROM table_name;查现有数据最大字节数,结果必须 ≤ 你计划设的新长度(注意是LENGTH(),不是CHAR_LENGTH())
ALTER TABLE MODIFY 报错?先清脏数据
MySQL 8.0+ 默认在 ALTER TABLE ... MODIFY 时校验全表已有数据。如果字段里存了早年宽松模式下写入的超长值(比如原 VARCHAR(50) 里有 78 字符),哪怕你扩到 VARCHAR(200),DDL 也会直接失败。
- 先跑
SELECT id, column_name FROM table_name WHERE LENGTH(column_name) > 200;找出超长记录 - 手动清理、截断或迁移这些数据,再执行
ALTER TABLE table_name MODIFY column_name VARCHAR(200); - 如果字段带前缀索引(如
INDEX(col(50))),SHOW INDEX FROM table_name WHERE Column_name = 'col';查Sub_part,超长后要同步重建索引,否则 INSERT 仍可能因索引前缀截断触发隐性异常
TEXT 比硬扩 VARCHAR 更可靠?要看场景
当字段语义就是“可能很长的文本”,比如用户评论、日志摘要、JSON 片段,TEXT 是更合理的选择:它不参与单行 65535 字节限制,走溢出页存储,也不受严格模式自动截断影响。
-
TEXT不支持默认值,如有需先ALTER TABLE table_name ALTER COLUMN column_name DROP DEFAULT; -
TEXT字段不能建全文索引以外的普通索引(除非指定前缀长度),且前缀索引上限是 3072 字节(utf8mb4下最多 768 字符) - 别用
TEXT存短字段——它会额外引入指针开销,查询性能略低于等效VARCHAR
应用层截断必须按字节,不是按字符
如果业务允许丢数据,要在代码里做截断,千万别用 substring(str, 0, 255) 这类按字符数截的方式。Java 中应调用 str.getBytes(StandardCharsets.UTF_8).length 获取真实字节数,再按需截取;PHP 用 mb_substr($str, 0, 255, 'UTF-8') 仍不够,得先 mb_strlen($str, 'UTF-8') + mb_convert_encoding() 模拟字节流校验。
- ORM(如 MyBatis、Hibernate)常缓存旧 schema,改完字段后务必清缓存或重启应用,否则预编译 SQL 仍按旧长度分配参数
- 临时关严格模式(
SET SESSION sql_mode = '')只适合调试,它会导致静默截断,Warning 很容易被忽略
真正容易被绕过的点是:同一个字段,在不同客户端连接编码下,LENGTH() 返回值完全不同;而报错永远以当前 session 的字符集为准算字节数。不锁死 client / connection / table / column 四层编码一致性,光调大字段只是把问题往后拖。











