mysql报“data too long”是服务端拒绝写入,需核对字段定义与数据字节长度;varchar按字节限制(utf8mb4下中文占4字节),用strlen而非mb_strlen校验;thinkphp不自动截断,需在beforewrite或sql层手动处理。

MySQL报 Data too long for column 时,不是PHP或ThinkPHP的问题
这个错误是MySQL服务端直接拒绝写入,ThinkPHP只是把错误原样抛出来。别急着改框架配置或模型验证,先确认真实数据长度和字段定义是否匹配。
常见错误现象:SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column 'title' at row 1 —— 注意错误里明确说了哪个字段、哪一行。
- 用
SHOW CREATE TABLE `your_table`查字段定义,重点看title是VARCHAR(50)还是TEXT - 用
strlen($data['title'])(不是mb_strlen)检查字节长度:MySQL 的VARCHAR限制按字节算,UTF8MB4 下一个中文占 4 字节 - ThinkPHP 默认不自动截断,也不会静默丢数据;它把超长内容原样交给 MySQL,然后被拦下来
ThinkPHP 6/7 中控制写入前截断的两种方式
想让超长数据自动截断而非报错,得自己加逻辑。框架不提供全局“自动截断”开关,因为这属于业务决策,不是通用行为。
- 在模型的
beforeWrite事件里处理:protected function beforeWrite(): void { if (isset($this->title) && strlen($this->title) > 50) { $this->title = substr($this->title, 0, 50); } } - 用
Db::raw()+SUBSTR在 SQL 层截断(适合批量插入):Db::name('article')->insert([ 'title' => Db::raw("SUBSTR('超长标题...', 1, 50)"), ]); - 注意:不能依赖
validate规则里的length:50,它只校验字符数(mb_strlen),而 MySQL 按字节判,二者在中文场景下不等价
字段类型选 VARCHAR 还是 TEXT?关键看查询和索引需求
别一看到“可能超长”就全改成 TEXT,它会带来隐性代价。
-
VARCHAR(255)支持前缀索引(如INDEX(title(100))),TEXT字段必须指定前缀长度才能建索引 -
TEXT列默认不参与内存临时表(MEMORY引擎),GROUP BY或ORDER BY可能被迫落盘,变慢 - 如果字段确实常超 255 字节且无需索引(如详情、日志),用
TEXT更稳妥;若要搜、要排序、要高频读,宁可收紧业务限制,坚持用VARCHAR
调试时容易漏掉的编码陷阱
同一串中文,在不同环节可能被不同方式编码,导致长度计算和实际入库不一致。
- PHP 文件本身编码要是
UTF-8 without BOM,否则strlen算出的字节数会多几个 BOM 字节 - 数据库连接必须显式设字符集:
'charset' => 'utf8mb4'(ThinkPHP 配置里),否则即使字段是utf8mb4,连接层也可能降级成utf8 - 前端传参如果是 URL 编码或 JSON,
$_POST或input()解析后已经是 UTF-8 字符串,直接strlen即可,不用再mb_convert_encoding
strlen 和 SHOW CREATE TABLE 对两遍,比翻框架文档快得多。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











