
问题现象
在 Typecho 后台删除已发布的文章时,页面报错:
Database Query Error: SQLSTATE[22003]: Numeric value out of range: 1690 BIGINT UNSIGNED value is out of range in '(`数据库名`.`typecho_metas`.`count` - 1)'
有趣的是:本地用 SQLite 测试完全正常,但部署到云服务器上用 MySQL 就报错。
原因分析
本地 vs 服务器的差异
本地开发环境用的是 SQLite 数据库,而云服务器用的是 MySQL。这两种数据库对无符号整型的处理方式不同:
- SQLite:不区分有符号/无符号,
0 - 1直接得到-1,不会报错
- MySQL:
BIGINT UNSIGNED列不允许负值,0 - 1直接溢出报错
错误链路
当在 Typecho 后台删除文章时,系统会执行以下操作:
- 从
typecho_contents表删除文章记录
- 从
typecho_relationships表删除分类/标签关联
- 更新
typecho_metas表的count字段:count = count - 1
- 删除关联评论等
第 3 步就是罪魁祸首。当某个分类或标签的 count 值已经为 0 时,执行 count - 1 就会产生负数,而 MySQL 的 UNSIGNED 列不允许负值,直接报 out of range。
为什么 count 会出现 0 甚至负数?
正常情况下,typecho_metas.count 应该等于当前关联的文章数量。但以下情况会导致计数不同步:
- 通过第三方插件(如 AI 发文插件)批量创建/删除文章
- 数据库直接操作或迁移导致的计数不一致
- AI_Bot 插件的
get_or_create_meta()方法创建分类时设置count = 0,后续虽然会尝试重新计算,但在某些场景下仍可能不同步
从本机 SQLite 数据库查到的数据就证实了这一点——有的分类 count 值已经是 -1 了,只是 SQLite 不报错而已。
解决方案
两步即可根治:
第一步:修改列类型为有符号整数
- ALTER TABLE `typecho_metas`
- MODIFY COLUMN `count` INT(10) SIGNED DEFAULT '0';
这一步把 count 列从 UNSIGNED 改为 SIGNED,允许负值存在,从根本上杜绝溢出报错。
第二步:重新校正所有分类/标签的计数值
- UPDATE `typecho_metas` m
- SET m.`count` = (
- SELECT COUNT(*)
- FROM `typecho_relationships` r
- WHERE r.`mid` = m.`mid`
- );
这一步根据 typecho_relationships 表中的真实关联数据,重新计算每个分类/标签的正确文章数。
第三步(可选):防止未来再次出现
如果你使用了 AI 自动发文等第三方插件,可以在插件创建分类/标签的代码中,在 count 更新时使用 GREATEST(count - 1, 0) 来防止负数溢出。不过一般来说,只要执行了前两步,这个问题就不会复现了。
总结
这个 Bug 的本质是 Typecho 在删除文章时对 metas.count 的递减操作,与 MySQL 的 UNSIGNED 约束冲突。修改列类型即可根治,不需要动 Typecho 核心代码。
如果你也遇到了同样的问题,按照上面的两步 SQL 命令执行即可解决。



