每一列是「名字、类型、属性」三样,属性写在类型后面,可以写好几个。 七个属性:DEFAULT、
NOT NULL、AUTO_INCREMENT、CHECK、UNIQUE、PRIMARY KEY、FOREIGN KEY,
外加一个只管说明的 COMMENT。
属性有两种写法:列级直接跟在列后面,表级集中写在最后。
复合主键、复合唯一只能写表级,因为一条约束同时管两列。
PRAGMA table_info 只能看见四样:类型、非空、默认值、主键。
CHECK 和 UNIQUE 看不到,得去看原始建表语句。
第 8 章建表时用过几个属性,这一章把它们一个一个拆开讲透。 这一章的每一篇都是「一个属性 + 一个坑」: 默认值管不到 update、非空不拦空串、自增写错了不报错、外键默认是关的。 这些坑的共同点是「不报错」——报错的坑不叫坑,叫提示。
一、属性的位置:写在类型后面
create table 表 (列 类型 属性, 列 类型 属性, …)。 Demo 里那张表四列,
name 同时带了非空和默认值,age 带了检查,email 带了唯一。
属性可以写好几个,用空格隔开就行,不需要逗号——逗号是用来分隔列的。
这一点新手常错:写成了 name TEXT, NOT NULL,那 NOT NULL 就被当成新的一列了。
二、七个属性,各管一件事
DEFAULT 管「不给值时填什么」,NOT NULL 管「不许是空」,
AUTO_INCREMENT 管「自己往上加」,CHECK 管「取值范围」。
UNIQUE 管「不许重复」,PRIMARY KEY 管「一行的身份证」,
FOREIGN KEY 管「指向别的表」。 最后一个 COMMENT 管「给人看」。
前七个数据库会真的拦你,最后一个不拦——
它只是说明文字,而且本沙箱连这个说明都存不好。 第 9.9 篇会单独撞一次。
三、列级写法与表级写法
列级:属性直接跟在列的类型后面,只能管这一列。 大多数属性都这么写。
表级:把所有列写完,再用逗号接约束。
PRIMARY KEY (a, b)、UNIQUE (a, b)、CHECK (…) 都能写成表级。
表级能干列级干不了的事:复合主键、复合唯一必须写表级,
因为一条约束同时管两列,没法挂在某一列后面。
另外给约束起名(CONSTRAINT ck_ab CHECK (…))也只有表级能写。
四、顺序无关,但习惯有讲究
同一个列上几个属性谁先谁后都行,
name TEXT NOT NULL DEFAULT '匿名' 和 name TEXT DEFAULT '匿名' NOT NULL 完全等价。
但读代码的人是按顺序理解的,习惯上排成「主键 → 非空 → 默认值 → 检查 → 唯一」,
这样一眼就能看出这列有多严格。
五、属性会互相打架
PRIMARY KEY 已经包含「非空 + 唯一」,再写 NOT NULL 是重复,写 UNIQUE 更是白写。
DEFAULT 和 NOT NULL 是最佳搭档:
非空保证「有值」,默认值保证「不给也有值」。
只写非空不写默认值,插入时漏了这列就直接报错。
六、PRAGMA 里只能看见四样
PRAGMA table_info(表) 给六列:
序号、列名、类型、是否非空(notnull)、默认值(dflt_value)、是否主键(pk)。
CHECK 和 UNIQUE 这两样在这里看不到——
它们不在 table_info 里,得去看 sqlite_master 里的原始建表语句。
这是查表结构时最容易被漏掉的一处:你以为看全了,其实少看了两条约束。
MySQL 的 DESCRIBE 能多给一个 Key 列,UNI 表示唯一、PRI 表示主键,
比这里多看见一样。 CHECK 两边都看不见,只能翻建表语句。
七、约束最好都起个名字
七个属性写一堆,报错只说「constraint failed」,不告诉你是谁拦的。 加了名字就不一样:
CONSTRAINT ck_age CHECK (age >= 0),报错会带上 ck_age。
名字还有个好处:以后想 DROP CONSTRAINT 得有名,
没名字的约束只能靠重建表来删。 这是「现在多打几个字,以后少干半小时」的典型例子。
八、动手 Demo:一张表四个属性,再看 PRAGMA 能看见什么
Demo 五段:建一张带四个属性的表、建一张用表级写法的表、看 PRAGMA 输出、再讲属性能不能互相打架。
第三段那张 PRAGMA 表是重点:它显示的信息比你以为的少。
import _q46 as Q
c = Q.blank()
# 一张表把几个属性都写上
Q.run(c, "CREATE TABLE demo_attr ("
"id INTEGER PRIMARY KEY, "
"name TEXT NOT NULL DEFAULT '匿名', "
"age INTEGER CHECK (age BETWEEN 0 AND 200), "
"email TEXT UNIQUE)")
# 表级写法:复合主键 + 具名约束
Q.run(c, "CREATE TABLE demo_tl (a TEXT, b TEXT, "
"PRIMARY KEY (a, b), "
"CONSTRAINT ck_ab CHECK (a <> b))")
# 属性在 PRAGMA 里能看见几样?
Q.show(c, "PRAGMA table_info(demo_attr)")
实跑输出:
--------------------------------------------------------------------
第四十二课:七个开关,一次说清
--------------------------------------------------------------------
属性的位置:写在类型后面
**建表时每一列是「名字、类型、属性」三样。** **属性写在类型的后面,可以写好几个。**
SQL> CREATE TABLE demo_attr (id INTEGER PRIMARY KEY, name TEXT NOT NULL DEFAULT '匿名',
age INTEGER CHECK (age BETWEEN 0 AND 200), email TEXT UNIQUE)
完成
七个属性,各管一件事
**`DEFAULT` 管「不给值时填什么」**,**`NOT NULL` 管「不许是空」**,
**`AUTO_INCREMENT` 管「自己往上加」**,**`CHECK` 管「取值范围」**。
**`UNIQUE` 管「不许重复」**,**`PRIMARY KEY` 管「一行的身份证」**,
**`FOREIGN KEY` 管「指向别的表」**。 **最后一个 `COMMENT` 管「给人看」。**
**前六个数据库会真的拦你,最后一个不拦——它只是说明文字。** **这一点第 50 课会单独撞一次。**
列级写法与表级写法
**属性可以直接跟在列后面**(列级),**也可以集中写在最后**(表级)。 上面那张表用的是列级。
SQL> CREATE TABLE demo_tl (a TEXT, b TEXT, PRIMARY KEY (a, b),
CONSTRAINT ck_ab CHECK (a <> b))
完成
**表级写法能干列级干不了的事**:**复合主键、复合唯一**都要写成表级,
**因为一条约束同时管两列,没法挂在某一列后面。**
顺序无关,但习惯有讲究
**同一个列上几个属性谁先谁后都行**,
`name TEXT NOT NULL DEFAULT '匿名'` 和 `name TEXT DEFAULT '匿名' NOT NULL` 等价。
**但读代码的人是按顺序理解的**,**习惯上排成「主键 → 非空 → 默认值 → 检查 → 唯一」**,
**这样一眼就能看出这列有多严。**
属性太多会互相打架
**`PRIMARY KEY` 已经包含「非空 + 唯一」**,再写 `NOT NULL` 是重复,写 `UNIQUE` 更是白写。
**`DEFAULT` 和 `NOT NULL` 经常一起用**:
**非空保证有值,默认值保证「不给也有值」。** **只写非空不写默认值,插入时漏了这列就直接报错。**
看一张表到底带了哪些属性
SQL> PRAGMA table_info(demo_attr)
cid name type notnull dflt_value pk
--------------------------------------------------------------------
0 id INTEGER 0 NULL 1
1 name TEXT 1 '匿名' 0
2 age INTEGER 0 NULL 0
3 email TEXT 0 NULL 0
(4 行)
**六列里能看出四样**:类型、是否非空(`notnull`)、默认值(`dflt_value`)、是否主键(`pk`)。
**`CHECK` 和 `UNIQUE` 这两样在这里看不到**——
**它们不在 `table_info` 里,得去看原始建表语句。** **这是查表结构时最容易被漏掉的一处。**
一条语句里塞太多属性,出问题很难查
**七个属性写在一列上,报错只说「constraint failed」**,**不告诉你是哪一条拦的。**
**所以约束最好都起个名字**:`CONSTRAINT ck_age CHECK (age >= 0)`,**报错时就会带上这个名字**,
一眼知道是谁拦的。
第一段四列四个属性:主键、非空 + 默认值、检查、唯一。
第二段表级写法:复合主键 PRIMARY KEY (a, b) 加具名约束 CONSTRAINT ck_ab CHECK (a <> b)。
第三段 PRAGMA 四行六列:name 的 notnull 是 1、dflt_value 是 '匿名',
id 的 pk 是 1。
CHECK 和 UNIQUE 在输出里一条都看不见——这就是「PRAGMA 看不全」的实证。
后面两段没有 SQL,讲属性顺序和「属性会互相打架」。
30 秒实验
name TEXT NOT NULL DEFAULT '匿名'里,两个属性之间要不要逗号?为什么?- 想给「两列一起」加唯一约束,能写在某一列后面吗?该写在哪?
PRAGMA table_info的输出里,能不能看出某列有UNIQUE?那要看哪里?
常见误区
- 属性之间加逗号。 逗号是分隔列的,加了以后后面那串会被当成新的一列, 建表可能不报错但结构全错。
- 以为
PRAGMA table_info就是全部结构。CHECK、UNIQUE、外键它都不显示,得去翻建表语句。 - 主键上再写
NOT NULL或UNIQUE。 主键本来就含这两条,写上去只是噪音, 还会让人以为约束是分开的两回事。
自测三问(点开对答案)
1. 七个属性各管什么?哪个不拦数据? —— `DEFAULT` 填默认值、`NOT NULL` 不许空、`AUTO_INCREMENT` 自动加、`CHECK` 管范围、 `UNIQUE` 不许重复、`PRIMARY KEY` 当身份证、`FOREIGN KEY` 指别的表。 `COMMENT` 只管说明,不拦。 2. 列级写法和表级写法分别适合什么? —— 列级管单列,多数属性都这么写;表级能写复合主键、复合唯一、具名约束,这些列级做不到。 3. 为什么约束要起名字? —— 报错时会带上名字,一眼知道是哪条拦的;而且不起名就没法单独删除这条约束,只能重建表。七个属性一个一个来。下一篇 9.2 默认值——它只在「你没提这一列」的时候生效。
- ☐ (本篇完,下一篇 9.2 默认值)