首页 / SQLite 入门教程 / CREATE TABLE:表结构与列类型

SQLite 入门教程

CREATE TABLE:表结构与列类型

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

sqliteCREATE TABLE建表

10. CREATE TABLE:表结构与列类型

本节目标:学完本章你能独立写出一个规范的建表语句,知道每一列由”列名 + 声明类型 + 约束”三部分组成,并学会用 IF NOT EXISTS 避免重复建表报错。

前面我们知道了 SQLite 的 5 种存储类和类型亲和性,这一章就动手把”表”真正建出来。先回想一个最关键的前提:SQLite 是 serverless(无服务进程)/ 零配置(zero-config) 的嵌入式数据库。它不像 MySQL 那样要先启动一个独立的服务器进程、再连上去建库。在 SQLite 里,数据库本身就是磁盘上的一个文件(默认后缀 .db.sqlite),你用 sqlite3 demo.db 打开它,文件不存在就当场创建一个。所以”建表”这个动作,本质是往这个文件里写入一段表结构定义,没有任何”服务器”在背后跑。

为什么需要 CREATE TABLE

关系型数据库里,数据按”表(table)“组织。一张表像一张二维表格:横着叫行(row,一条记录),竖着叫列(column,一个字段)。在往表里塞数据之前,得先告诉 SQLite 这张表有几列、每列叫什么、偏好什么类型——这就是 CREATE TABLE 干的事。它只定义”骨架”,不存具体数据。

基本语法长什么样

一个最朴素的建表语句结构是:

CREATE TABLE 表名(
  列1 声明类型 [列约束],
  列2 声明类型 [列约束],
  ...
  [表级约束]
);

括号里每一行定义一列。每一列由三部分组成:列名声明类型、可选的列约束。声明类型决定亲和性(回顾第 09 章),列约束用来加规矩(下一章专讲,本章先认识它们的位置)。

我们拿教程全局统一的示例表来练手。用户表 users 和订单表 orders 会贯穿后续所有章节:

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

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

这里 users 有 4 列:id 是整数主键、name 是必填文本、age 是可空整数、email 是文本。orders 有 4 列:订单号、所属用户 id、金额(REAL 浮点,因为钱常有小数)、下单时间(注意我们用 TEXT 存时间字符串,再次印证 SQLite 没有日期类型)。

Note

重点提示:表名不能以 sqlite_ 开头,那是 SQLite 内部保留前缀。另外,列名、表名如果和 SQL 关键字撞车,可以用双引号 " 或反引号 ` 包起来,但入门阶段建议直接取不会冲突的名字,省心。

声明类型怎么写才不踩亲和性的坑

结合第 09 章的 5 条规则,写声明类型时记住三点:

  • 想要整数列,写 INTEGER(或任何含 INT 的名字)。注意:只有写 INTEGER PRIMARY KEY 才能拿到”rowid 别名”这个特殊待遇(第 12 章细讲),写 INTBIGINT 不会触发。
  • 想要文本列,写 TEXTVARCHAR(n)CHAR(n)——这些因为含 CHAR/TEXT 而归入 TEXT 亲和性,括号里的数字被忽略。
  • 想要精确浮点,写 REAL/FLOAT/DOUBLE;想要”随便”就写 BLOB 或不写。

很多初学者纠结”到底写 VARCHAR 还是 TEXT”。在 SQLite 里它们没区别,都只是 TEXT 亲和性的不同名字。别被括号里的长度迷惑。

IF NOT EXISTS:防御式建表

新手最常遇到的报错之一:对一个已存在的表再执行一遍 CREATE TABLE,SQLite 会直接报错 table xxx already exists,并且整个语句中止。在脚本、初始化程序、重复执行的迁移文件里,这很烦人。

解决办法是加 IF NOT EXISTS:只有当表不存在时才建,存在就安静跳过。

CREATE TABLE IF NOT EXISTS users(
  id    INTEGER PRIMARY KEY,
  name  TEXT    NOT NULL,
  age   INTEGER,
  email TEXT
);

这样无论脚本跑多少次都安全。命令行里试一下:

sqlite> CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT);
sqlite> CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT);
sqlite> -- 第二次不会报错,只是什么都不做
Tip

实用技巧:把你应用的数据库初始化脚本里所有 CREATE TABLE 都默认写成 CREATE TABLE IF NOT EXISTS。这是零成本的好习惯,能避免”程序重启就崩在重复建表”的经典事故。

怎么确认表建好了

SQLite 命令行提供了几个查看结构的命令:

sqlite> .tables
orders  users
sqlite> .schema users
CREATE TABLE users(
  id    INTEGER PRIMARY KEY,
  name  TEXT    NOT NULL,
  age   INTEGER,
  email TEXT
)
sqlite> PRAGMA table_info(users);

.tables 列出当前库所有表;.schema 表名 打印建表语句;PRAGMA table_info(表名) 则以表格形式列出每列的编号、名字、声明类型、是否可空、默认值、是否主键。查表结构是排查”为什么插不进去、为什么类型不对”的第一步。

完整建表一次过

把两个示例表连同防御式写法一起执行:

sqlite> CREATE TABLE IF NOT EXISTS users(
   ...>   id    INTEGER PRIMARY KEY,
   ...>   name  TEXT    NOT NULL,
   ...>   age   INTEGER,
   ...>   email TEXT
   ...> );
sqlite> CREATE TABLE IF NOT EXISTS orders(
   ...>   id         INTEGER PRIMARY KEY,
   ...>   user_id    INTEGER,
   ...>   amount     REAL,
   ...>   created_at TEXT
   ...> );
sqlite> .tables
orders  users

看到 orders users 两行,说明骨架立好了,可以往里塞数据了——那是后面 INSERT 章节的事。

进阶可选:临时表、附加库与 schema 前缀

CREATE TABLE 还有两个实用变体,了解后能应对更多场景:

  • 临时表 CREATE TEMP TABLE:只在当前连接存在,断开后自动消失,不写进正式数据库文件。适合做中间计算结果、导入暂存。语法和普通建表一模一样,只是前面多了 TEMPTEMPORARY
  • 指定 schema 前缀:当你用 ATTACH DATABASE '其他.db' AS other 挂接了第二个数据库文件,建表时可以写 CREATE TABLE other.表名(...),明确把表建到那个文件里。不写前缀就建到主库(main)。

这两个写法不改变列定义规则,只是决定”表建在哪、活多久”,是普通建表的自然延伸。

常见误区与边界

  • 误区一:认为写了 VARCHAR(50) 就能限制字符串最长 50。不会,SQLite 不强制长度。
  • 误区二:认为 CREATE TABLE 会”连库一起建”。库是文件,是你打开 sqlite3 文件名 时就存在的;CREATE TABLE 只在已有文件内加一张表。
  • 边界:默认每张表有一个隐藏的 rowid 整数列(除非建表时加 WITHOUT ROWID,那是进阶主题)。这个 rowid 和我们写的 INTEGER PRIMARY KEY 关系匪浅,第 12 章揭晓。

类比小结

建表就像先画”收纳柜的格子标签”:你决定有几个格子、每个格子贴什么颜色的标签(声明类型=亲和性建议)、哪些格子不允许空着(约束)。但 SQLite 这个柜子很随和——标签只是建议,真塞进不该塞的东西它通常也不吼你(除非撞上约束)。而 IF NOT EXISTS 相当于给柜门贴了张”已有勿建”的便签,重复装修也不会砸坏房子。理解建表只是”写骨架”,你就不再怕它了。

Warning

常见坑:别把别的数据库的”建库再建表”流程照搬到 SQLite。SQLite 没有独立服务器,也没有单独的”CREATE DATABASE”步骤——数据库即文件,打开文件即建库,建表只是往文件里写结构。若硬写 CREATE DATABASE 会直接报语法错误。