create table 表名 (列名 类型, 列名 类型, …),
每一列三样东西:名字、类型、属性。 名字和类型必填,属性可选。
Demo 里那张表一次用上五个属性:PRIMARY KEY、NOT NULL、DEFAULT、CHECK、UNIQUE。
第 9 章逐个讲,这里先看它们怎么排。
IF NOT EXISTS 让脚本能重复跑——
跑第二遍不该因为「表已存在」就中断整批。 初始化脚本里几乎是标配。
前两篇是「看」和「讲」,这一篇开始真动手建东西。 建表是全书唯一一个「一次写对最省事」的动作: 表建好了再改,比一开始就写对麻烦十倍——第 8.5 篇就是讲改表有多麻烦。
一、最小的一张表
create table demo_min (id integer primary key, name text),一行就建完。 两列:
id 是整数主键,name 是文本。
括号里是列的清单,每列「名字 类型」两个词,多列用逗号隔开。
最后那个属性可以不给,上面这行只给 id 加了主键。
注意最后不要多打逗号:(a int, b text,) 这种写法在多数数据库里都报错。
列很多时容易漏,所以长语句一般一列一行写。
二、一张正经的表:把属性也用上
Demo 第二段建了 demo_stu,五列五个属性:id INTEGER PRIMARY KEY、name TEXT NOT NULL、
gender TEXT DEFAULT '未知'、age INTEGER CHECK (age >= 0)、phone TEXT UNIQUE。
这五个属性各有各的活:主键唯一标识一行;非空不许留空;默认值在不给值时兜底;
检查限定取值范围;唯一不许重复。
它们写在「类型」后面,位置不固定、顺序也不敏感,
但习惯上按「主键 → 非空 → 默认值 → 其他约束」排,读起来顺。
三、拆开看:PRAGMA table_info
建完立刻拆开看一遍,这是最有效的自查。
Demo 里 PRAGMA table_info(demo_stu) 给出六列:序号、列名、类型、是否非空、默认值、是否主键。
第二列 notnull 是 1 就表示非空,
最后一列 pk 是 1 就表示主键。 输出里 id 的 pk 是 1,name 的 notnull 是 1,
其余都是 0。
MySQL 里对应的是 DESCRIBE 表名,给的也是六列,
字段名不同、意思差不多——第 8.4 篇专门对照。
四、唯一与非空是两件事
看 phone 那一行:notnull 是 0,但它是 UNIQUE。 这两个属性经常被混为一谈。
非空管的是「这一格必须有值」;
唯一管的是「这一列里不许有两个一样的值」。 一个管单身,一个管不重样。
关键在于:唯一允许多个 NULL。 因为 NULL 不等于 NULL——两个「空」不算重复。
所以一列加了 UNIQUE 却允许为空,就能塞进任意多个 NULL。
想让「唯一且必须有值」,两个属性要一起写。
五、IF NOT EXISTS:重复建表不报错
同一句 create table demo_min 跑第二遍,报 table demo_min already exists。 这是对的——
它没让你把已有的表覆盖掉。
加 if not exists 就不报了:「不存在才建」,已经存在就静默跳过。
这个写法在初始化脚本里几乎是标配:
脚本要能重复跑,跑第二遍不该因为「表已存在」就中断整批。 本书后面建表的地方都会带它。
六、建表时最容易踩的三处
一、类型写错。 本沙箱(SQLite)的类型很宽松,写 int、INTEGER、甚至 abc 都能建表;
MySQL 会严格检查,写错就报错。 别因为这里能过,就以为 MySQL 也能过。
二、忘记主键。 没有主键的表能建,
但改数据、做关联时会很难受——你没法用一句话准确指到某一行。
第 9 章会讲为什么几乎每张表都该有主键。
三、约束写太晚。 表里已经有脏数据时再加约束,加不上去,因为老数据不满足。
约束要在建表时就想清楚,别等出事再补。
七、动手 Demo:从两列的表到五列五个属性
Demo 五段:先建最小的一张表,再建一张五个属性都用上的,然后 PRAGMA 拆开看、撞一次重复建表、
用 IF NOT EXISTS 化解。
中间那张 PRAGMA 的表是这一篇的重点——它把「你写的属性」翻译成「数据库实际记的东西」。
import _q46 as Q
c = Q.con()
# 最小的一张表
Q.run(c, "CREATE TABLE demo_min (id INTEGER PRIMARY KEY, name TEXT)")
# 五个属性都用上
Q.run(c, "CREATE TABLE demo_stu ("
"id INTEGER PRIMARY KEY, "
"name TEXT NOT NULL, "
"gender TEXT DEFAULT '未知', "
"age INTEGER CHECK (age >= 0), "
"phone TEXT UNIQUE)")
# 拆开看每一列:序号、列名、类型、非空、默认值、主键
Q.show(c, "PRAGMA table_info(demo_stu)")
# 重复建表:加 IF NOT EXISTS 就不报错
Q.run(c, "CREATE TABLE IF NOT EXISTS demo_min (id INTEGER PRIMARY KEY)")
实跑输出:
--------------------------------------------------------------------
第三十七课:建表——列名、类型、属性,三样都要写清楚
--------------------------------------------------------------------
最小的一张表
SQL> CREATE TABLE demo_min (id INTEGER PRIMARY KEY, name TEXT)
完成
**`create table 表名 (列名 类型, 列名 类型)`**——**每一列三样东西:名字、类型、(可选的)属性。**
**列名和类型是必填的,属性可以不给。** **上面这张表两列,`id` 加了主键属性。**
一张正经的表:把属性也用上
SQL> CREATE TABLE demo_stu (id INTEGER PRIMARY KEY, name TEXT NOT NULL,
gender TEXT DEFAULT '未知', age INTEGER CHECK (age >= 0), phone TEXT UNIQUE)
完成
SQL> SELECT sql FROM sqlite_master WHERE name = 'demo_stu'
demo_stu 的建表语句:
CREATE TABLE demo_stu (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
gender TEXT DEFAULT '未知',
age INTEGER CHECK (age >= 0),
phone TEXT UNIQUE
)
(1 行)
**这一句里出现了五个属性**:`PRIMARY KEY` 主键、`NOT NULL` 非空、`DEFAULT` 默认值、
`CHECK` 检查、`UNIQUE` 唯一。 **第 9 章逐个讲,这里先看它们怎么排在一行里。**
拆开看:每一列到底有什么
SQL> PRAGMA table_info(demo_stu)
cid name type notnull dflt_value pk
---------------------------------------------------------------------
0 id INTEGER 0 NULL 1
1 name TEXT 1 NULL 0
2 gender TEXT 0 '未知' 0
3 age INTEGER 0 NULL 0
4 phone TEXT 0 NULL 0
(5 行)
**六列信息:序号、列名、类型、是否非空、默认值、是否主键。**
**第二列(`notnull`)是 1 就表示非空**,**最后一列 `pk` 是 1 就表示主键。**
**注意 `phone` 那一行:`notnull` 是 0,但它是 UNIQUE。** **唯一和非空是两件事**——
**唯一只要求「不能有两个一样的」,允许多个 NULL**(因为 NULL 不等于 NULL)。
IF NOT EXISTS:重复建表不报错
SQL> CREATE TABLE demo_min (id INTEGER PRIMARY KEY)
报错:table demo_min already exists
SQL> CREATE TABLE IF NOT EXISTS demo_min (id INTEGER PRIMARY KEY)
完成
**第二句不报错,因为加了 `IF NOT EXISTS`**——**「不存在才建」,已经存在就跳过。**
**这个写法在初始化脚本里几乎是标配**:脚本要能重复跑,
**跑第二遍不该因为「表已存在」就中断整批。** **本书后面建表的地方都会带它。**
建表时最容易踩的三处
**一、类型写错**:本沙箱(SQLite)的类型很宽松,**写 `int`、`INTEGER`、甚至 `abc` 都能建表**;
**MySQL 会严格检查,写错就报错。** **别因为这里能过,就以为 MySQL 也能过。**
**二、忘记主键**:没有主键的表能建,但**改数据、做关联时会很难受**。
**第 9 章会讲为什么几乎每张表都该有主键。**
**三、约束写太晚**:表里已经有脏数据时再加约束,**加不上去**(老数据不满足)。
**约束要在建表时就想清楚,别等出事再补。**
第一段两列,id 是主键、name 是文本,建完就完。
第二段五列五个属性,
建完把 sqlite_master 里的原始语句拆行打出来——原句是一长行,这里按列拆开看更清楚。
第三段 PRAGMA 五行:id 的 pk 是 1,name 的 notnull 是 1,
gender 的默认值是 '未知',其余两列都是 0 和 NULL。
第四段撞重复建表,报 already exists;
第五段加 IF NOT EXISTS 就不报了——影响行数是负的,说明没执行任何动作。
30 秒实验
CREATE TABLE t (a TEXT UNIQUE)之后,能插入几行a为 NULL 的记录?为什么?CREATE TABLE t2 (a INT, b TEXT)少了什么?会带来什么麻烦?- 把同一段建表脚本连跑两遍,第二遍会报什么错?怎么让它不报?
常见误区
- 以为 UNIQUE 就等于「必须有值」。 唯一只管不重样,允许多个 NULL;要非空得再加
NOT NULL。 - 在本沙箱能过就以为 MySQL 也能过。 这里类型写
abc都能建表,MySQL 直接报错。 - 先建表、等有数据了再补约束。 老数据不满足约束时补不上去,只能先清数据再补,代价大得多。
自测三问(点开对答案)
1. 建表时每一列要写哪三样东西?哪些是必填的? —— 列名、类型、属性。前两个必填,属性可选,可以有多个。 2. `notnull` 是 0 但列上有 UNIQUE,能插入几个 NULL? —— 任意多个。NULL 不等于 NULL,两个空值不算重复,唯一约束不拦它们。 3. `IF NOT EXISTS` 解决的是什么问题? —— 让建表语句可以重复执行,表已存在时静默跳过,脚本跑第二遍不会中断。表建好了,下一篇学怎么「看」它:8.4 三条 MySQL 语句与三个等价写法。
- ☐ (本篇完,下一篇 8.4 查看表)