DEBUG.WIKI
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
雨云;给项目一台云服务器
RackNerd;$21.99/年 · 1GB KVM

SQLite 建表报 AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY

SQLite 的 BIGINT 主键不能直接使用 AUTOINCREMENT。核对生成 SQL 的精确类型名,区分自动编号与不复用编号,并处理 ORM 映射和已有表迁移。

SQLite 建表或执行 ORM 迁移时报 AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY,实际生成的列定义却已经有 PRIMARY KEY,例如 id BIGINT PRIMARY KEY AUTOINCREMENT。这里缺少的不是主键标记,而是 SQLite 对这个特殊主键的精确类型名要求。

本文针对普通 SQLite rowid 表。WITHOUT ROWID 表不能使用 AUTOINCREMENT;复合主键也不能靠给其中一列追加这个关键字获得同样行为。

查看生成的 SQL,不只看模型里的整数类型

先保存失败的完整建表语句、实际 SQLite 版本和 ORM 依赖锁定信息。重点看主键列最终被生成为 INTEGER、INT 还是 BIGINT。模型里叫“64 位整数”,并不能说明 SQL 中采用了哪种拼写。

在普通 rowid 表中,单列主键声明的类型名精确为 INTEGER 时,才通常成为隐藏 rowid 的别名。大小写不影响这个判断;INT、BIGINT 即使具有整数亲和性,也不是这里的等价拼写。列级 INTEGER PRIMARY KEY DESC 另有历史例外,不要在修复时顺手添加 DESC。

SQLite 的 rowid 本身是有符号 64 位整数。将这类主键在 SQLite 后端生成为 INTEGER,并不是把它缩成其他数据库常见的 32 位整型。但应用驱动与模型仍须能够接收对应范围的值。

新建普通表时使用对应声明

下面只用于新的、可丢弃的测试数据库,名称为示例:

CREATE TABLE demo_item (
    id INTEGER PRIMARY KEY,
    label TEXT NOT NULL
);

INSERT INTO demo_item (label) VALUES ('first');
SELECT id, label FROM demo_item;

省略 id 时,SQLite 已经会自动分配一个未使用的 rowid,并不需要为了“插入时自动生成编号”额外加 AUTOINCREMENT。

如果业务确实要求自动编号不再复用该表以前已提交记录使用过的 rowid,新表声明才采用:

CREATE TABLE demo_item_history (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    label TEXT NOT NULL
);

这改变编号分配算法,并带来额外维护开销。它不保证编号连续无空洞,回滚事务用过的编号也可能再次使用。不要用这个关键字承诺业务流水号绝不缺号。

仅从错误语句删除 AUTOINCREMENT,可能让 BIGINT PRIMARY KEY 建表成功,却仍没有得到作为 rowid 别名的自动编号列。应先决定需要哪一种主键语义,再修改完整声明。

修复生成层,避免下次迁移又产生 BIGINT

直接维护 SQL 时,修改对应 SQLite 建表语句。ORM 或查询构建器生成 SQL 时,应在 SQLite 后端的类型映射、迁移定义或依赖版本中解决,不能把所有数据库的 64 位主键都统一改成某种较小类型。

SeaQuery 0.30.3 已包含 SQLite BIGINT PRIMARY KEY AUTOINCREMENT 的生成修复。如果使用这一依赖链,核对锁文件实际解析到的版本,以及最终输出是否已改成 INTEGER。不要只升级外层 ORM 的声明版本,也不要假定它一定采用了刚安装的查询构建器。

升级前保留依赖锁文件和迁移差异,并检查对应版本的兼容变化。更高版本仍输出错误类型时,应检查是否执行了旧迁移文件、另一份构建产物或自定义 SQL,而不是继续盲目升级。

已有数据库不能靠重复 CREATE TABLE 改列类型

CREATE TABLE IF NOT EXISTS 不会把已有主键自动换成新的声明。对于已经有业务数据的表,先读取实际建表定义并与迁移记录比较;不要为了让初始化脚本通过而删除整库或现有表。

确实需要改变已有表结构时,应另外制定可恢复的迁移,核对主键值、外键、索引、触发器与历史数据。这里的报错修复不提供直接重建生产表的快捷脚本,也不需要手动修改 sqlite_sequence 来绕过语法限制。

验收至少覆盖新库初始化、已有库升级,以及省略主键的插入是否得到预期编号。若业务依赖不复用编号,还要在隔离数据中验证对应删除与重新插入流程。回退依赖或迁移文件不会撤销已经提交的结构变更,应使用事先准备的迁移回退或恢复方案。