🦏 第8章 表的操作
📄 一句话概括

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 秒实验

  1. CREATE TABLE t (a TEXT UNIQUE) 之后,能插入几行 a 为 NULL 的记录?为什么?
  2. CREATE TABLE t2 (a INT, b TEXT) 少了什么?会带来什么麻烦?
  3. 把同一段建表脚本连跑两遍,第二遍会报什么错?怎么让它不报?

常见误区

  1. 以为 UNIQUE 就等于「必须有值」。 唯一只管不重样,允许多个 NULL;要非空得再加 NOT NULL。
  2. 在本沙箱能过就以为 MySQL 也能过。 这里类型写 abc 都能建表,MySQL 直接报错。
  3. 先建表、等有数据了再补约束。 老数据不满足约束时补不上去,只能先清数据再补,代价大得多。
自测三问(点开对答案) 1. 建表时每一列要写哪三样东西?哪些是必填的? —— 列名、类型、属性。前两个必填,属性可选,可以有多个。 2. `notnull` 是 0 但列上有 UNIQUE,能插入几个 NULL? —— 任意多个。NULL 不等于 NULL,两个空值不算重复,唯一约束不拦它们。 3. `IF NOT EXISTS` 解决的是什么问题? —— 让建表语句可以重复执行,表已存在时静默跳过,脚本跑第二遍不会中断。

表建好了,下一篇学怎么「看」它:8.4 三条 MySQL 语句与三个等价写法。

相关笔记