首页 / SQLite 入门教程 / 默认值与自增主键

SQLite 入门教程

默认值与自增主键

本教程共 50 篇 · 第 12 篇 · 更新于 2026-07-31

sqlite自增主键AUTOINCREMENTDEFAULT

12. 默认值与自增主键

本节目标:学完本章你能正确写出”自增主键”——知道默认就该用 INTEGER PRIMARY KEY 隐式自增,理解 AUTOINCREMENT 只是可选的补充,并掌握 DEFAULT 给列设默认值。

这一章要解开 SQLite 入门者最容易被误导的两个点:一是”自增主键到底怎么写”,二是”列的默认值怎么给”。很多教程一上来就教 INTEGER PRIMARY KEY AUTOINCREMENT,让人以为少了 AUTOINCREMENT 就不自增。真相恰恰相反:在 99% 的场景里,你根本不需要 AUTOINCREMENT 关键字。

DEFAULT:当没给值时填什么

先看简单的 DEFAULT。建表时给某列加 DEFAULT 值,意思是:插入一行时如果没指定这一列,SQLite 就自动用默认值填空。

CREATE TABLE users(
  id      INTEGER PRIMARY KEY,
  name    TEXT NOT NULL,
  age     INTEGER,
  email   TEXT,
  status  TEXT DEFAULT 'active',
  created_at TEXT DEFAULT CURRENT_TIMESTAMP
);

这里 status 默认 'active'created_at 默认用 CURRENT_TIMESTAMP(SQLite 内置表达式,返回当前时间的 ISO8601 文本)。插入时不写这两列试试:

sqlite> INSERT INTO users(name, email) VALUES('张三','a@b.com');
sqlite> SELECT id, name, status, created_at FROM users;
1|张三|active|2026-07-31 10:22:05

DEFAULT 的几条要点:

  • 默认值可以是字面量(0'active'),也可以是简单的常量表达式(如 CURRENT_TIMESTAMP(datetime('now')))。
  • 如果显式写了 NULL(而不是”省略这一列”),DEFAULT 不会生效——NULL 就是 NULL。
  • DEFAULT 和 NOT NULL 可以共存:先填默认,再保证不为空,常见于”创建时间""状态标记”这类字段。

关于 DEFAULT 的表达式写法,有两个容易踩的点要提前讲清。第一,凡是函数或运算形式的默认值,必须套在括号里,例如 DEFAULT (datetime('now'))DEFAULT (abs(-3));如果漏掉括号写成 DEFAULT datetime('now'),SQLite 会把 datetime 当成列名去解析而直接报语法错。第二,这里的”表达式”指的是插入时刻可求值的常量表达式,它不能引用表中的其他列、也不要依赖随机状态(除 CURRENT_TIMESTAMP 这类时间函数外)。善用表达式默认值,可以把”默认值是另一个计算结果”的逻辑直接固化在建表语句里,省去一段应用代码。换句话说,DEFAULT 不只填”一个固定的值”,也能填”一个在插入时才算出来的值”。

Tip

实用技巧:给”创建时间""是否删除”这类字段设 DEFAULT,比在应用代码里每次手工填更省心,也避免忘了填导致 NULL。注意 CURRENT_TIMESTAMP 给的是 UTC 时间文本,展示时再按需要转换时区。

自增主键的真相:INTEGER PRIMARY KEY 已经自带自增

接下来是重头戏。回忆第 10 章提过:默认每张表都有一个隐藏的整数列 rowid,它唯一标识每行,并且自动递增——你插入新行不指定 rowid,SQLite 就给它分配”当前最大 rowid + 1”,从 1 开始。

关键来了:如果你把某一列声明为 INTEGER PRIMARY KEY,这一列就不再是普通列,而是变成了 rowid别名(alias)。换句话说,idrowid 是同一个人、两个名字。于是 id 自动获得了 rowid 的自增能力:

CREATE TABLE users(
  id   INTEGER PRIMARY KEY,
  name TEXT NOT NULL
);

连续插入不指定 id:

sqlite> INSERT INTO users(name) VALUES('张三');
sqlite> INSERT INTO users(name) VALUES('李四');
sqlite> SELECT id, name FROM users;
1|张三
2|李四

看到了吗?没写 AUTOINCREMENT,id 也自动变成 1、2、3…… 这就是 SQLite 推荐的”自增主键”写法,零额外开销。

Note

重点提示:只有写成 INTEGER PRIMARY KEY 才会成为 rowid 别名。写 INT PRIMARY KEYBIGINT PRIMARY KEY 都不会触发——必须含完整的 INTEGER 这个词。另外 INTEGER PRIMARY KEY DESC(降序)也不会成为别名,这是一种少见但坑人的写法,入门阶段别碰。

AUTOINCREMENT:可选的关键字,不是必需品

AUTOINCREMENT 到底干嘛用?它解决的是 rowid 默认行为的一个边缘情况:rowid 在删除一些行后,会复用被删掉的 id。例如表里有 1、2、3,你删了 3,再插一行,新行可能又拿到 3。

对绝大多数应用,这完全无所谓——你本来就不该拿 id 当业务含义(比如”3 号订单”被复用没关系,因为它是系统内部标识)。但少数场景(如审计、合规)要求”id 永不重复、删了也不许再出现”,这时才用 AUTOINCREMENT

CREATE TABLE users(
  id   INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT NOT NULL
);

加了这个关键字后,SQLite 会用一个叫 sqlite_sequence 的内部表记住”已经发到的最大 id”,下次永远发比它更大的数,绝不回头复用。

代价是什么?官方文档明确说:AUTOINCREMENT 会带来额外的 CPU、内存、磁盘空间和磁盘 I/O 开销,能不用就不用。具体坏处有两点:

  1. 多了 sqlite_sequence 依赖:数据库里多一张系统表来记账,建表、插入都要额外读写它。
  2. 行为更”脆”:一旦 rowid 触达上限 9223372036854775807(极大,几乎碰不到),普通 rowid 还能找别的空号用,而 AUTOINCREMENT 表会直接报 database or disk is full 错误,因为”永远递增”已经无号可增。

所以请把顺序记牢:默认写 INTEGER PRIMARY KEY;只有”绝不复用 id”是硬需求时才加 AUTOINCREMENT

Warning

常见坑:很多教程把 AUTOINCREMENT 写成自增主键的”标配”,这是误导。正确认知是——自增能力来自 INTEGER PRIMARY KEY(rowid 别名),AUTOINCREMENT 只是防止 id 复用的可选补充。盲目加上它,只是白白吃掉性能和多一张内部表,却换不来任何你真正需要的收益。另外注意 AUTOINCREMENT 必须紧跟在 INTEGER PRIMARY KEY 之后,单独写 INTEGER AUTOINCREMENT 是语法错误。

指定 id 也能覆盖自增

自增是”你不填时”的行为。如果你显式给 id 赋值,SQLite 就听话用你的值:

sqlite> INSERT INTO users(id, name) VALUES(100, '王五');
sqlite> SELECT id, name FROM users;
1|张三
2|李四
100|王五

之后不指定 id 再插,会从”当前最大值 + 1”继续,也就是 101。这也是 rowid alias 的标准表现。

对比一览:INTEGER PRIMARY KEY vs AUTOINCREMENT

用一张表把两者差异摆清楚,避免再混:

写法是否自增是否复用已删 id额外开销何时用
INTEGER PRIMARY KEY是(rowid 别名)会复用默认、绝大多数场景
INTEGER PRIMARY KEY AUTOINCREMENT永不复用有(sqlite_sequence)仅当”id 永不可重复”是硬需求

记住结论:自增能力两种写法都有,差别只在”删过的号还让不让人用”。普通业务用第一行就够。

一个例外:WITHOUT ROWID 表

前面说 INTEGER PRIMARY KEY 是 rowid 别名,前提是这张表有 rowid。如果你建表时加了 WITHOUT ROWID(为某些海量小表省空间、改 B 树结构),表里根本没有 rowid,此时 INTEGER PRIMARY KEY 就退化成普通主键,不再自动自增,必须自己管理 id。这是进阶主题,入门阶段默认不写 WITHOUT ROWID,所以”INTEGER PRIMARY KEY 隐式自增”的套路始终成立。

DEFAULT 与自增能否共存

可以。id 走自增(本质 DEFAULT 是 rowid 的自动分配),其他列走 DEFAULT 字面量,互不冲突:

CREATE TABLE orders(
  id         INTEGER PRIMARY KEY,
  user_id    INTEGER,
  amount     REAL DEFAULT 0,
  created_at TEXT DEFAULT CURRENT_TIMESTAMP
);

类比小结

INTEGER PRIMARY KEY 想成”带着自动编号机的抽屉”:你往里放东西,编号机自动贴 1、2、3 号,根本不用你操心——这就是隐式自增。而 AUTOINCREMENT 是”编号机旁边加了一本永不撕页的账本”,保证号码只增不减、用过就作废;账本要人看管(开销),而且号码用尽就卡死。普通人家用自动编号机就够了,没必要天天抱着账本。DEFAULT 则像”忘了填就按惯例补上”的便签——没写状态就默认 active,没写时间就盖当前时间戳。

Note

重点提示:记住一句口诀——自增靠 INTEGER PRIMARY KEY(rowid 别名),AUTOINCREMENT 仅用于”永不复用 id”的少见需求,且需承担 sqlite_sequence 开销。这是第 09 章”动态类型”之后第二个高频误解点,面试和实战都常考。