在 Typecho 后台删除已发布的文章时,页面报错Database Query Error

2026年7月29日17:35:47 发表评论
微信搜一搜 ts小陈

在 Typecho 后台删除已发布的文章时,页面报错Database Query Error

问题现象

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,不会报错
  • MySQLBIGINT UNSIGNED 列不允许负值,0 - 1 直接溢出报错

错误链路

当在 Typecho 后台删除文章时,系统会执行以下操作:

  1. typecho_contents 表删除文章记录
  1. typecho_relationships 表删除分类/标签关联
  1. 更新 typecho_metas 表的 count 字段count = count - 1
  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 不报错而已。

解决方案

两步即可根治:

第一步:修改列类型为有符号整数

  1. ALTER TABLE `typecho_metas`
  2. MODIFY COLUMN `countINT(10) SIGNED DEFAULT '0';

这一步把 count 列从 UNSIGNED 改为 SIGNED,允许负值存在,从根本上杜绝溢出报错。

第二步:重新校正所有分类/标签的计数值

  1. UPDATE `typecho_metas` m
  2. SET m.`count` = (
  3.     SELECT COUNT(*)
  4.     FROM `typecho_relationships` r
  5.     WHERE r.`mid` = m.`mid`
  6. );

这一步根据 typecho_relationships 表中的真实关联数据,重新计算每个分类/标签的正确文章数。

第三步(可选):防止未来再次出现

如果你使用了 AI 自动发文等第三方插件,可以在插件创建分类/标签的代码中,在 count 更新时使用 GREATEST(count - 1, 0) 来防止负数溢出。不过一般来说,只要执行了前两步,这个问题就不会复现了。

总结

这个 Bug 的本质是 Typecho 在删除文章时对 metas.count 的递减操作,与 MySQL 的 UNSIGNED 约束冲突。修改列类型即可根治,不需要动 Typecho 核心代码。

如果你也遇到了同样的问题,按照上面的两步 SQL 命令执行即可解决。

ts小陈

发表评论(不允许含有网址!)

:?: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :cry: :mrgreen: :neutral: :razz:

😀

已登录用户不需要填写以下内容