CREATE TABLE:表结构与列类型
本教程共 50 篇 · 第 10 篇 · 更新于 2026-07-31
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 章细讲),写INT、BIGINT不会触发。 - 想要文本列,写
TEXT或VARCHAR(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:只在当前连接存在,断开后自动消失,不写进正式数据库文件。适合做中间计算结果、导入暂存。语法和普通建表一模一样,只是前面多了TEMP或TEMPORARY。 - 指定 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会直接报语法错误。