本章将讨论 PostgreSQL 的约束,包括:主键约束、外键约束、唯一约束、非空约束、 CHECK 约束。
非空约束
在数据库理论中,NULL
代表未知或信息缺失。NULL
与零值(空字符串、数字0等)不是一个概念。
NULL
很特别,它不等于任何东西,包括它自己。NULL = NULL
的结果是 NULL
,而不是你期望的TRUE
。要检查是否为 NULL
,需要使用 IS NULL
或 IS NO NULL
,而不是 =
或 <>
。
非空约束是指,限定某个字段的值不能为 NULL
:
CREATE TABLE 表名(
...
字段名 数据类型 NOT NULL,
...
);
在创建表的时候,如果要给某个字段指定非空约束,可以参照上面的语法。
如果要给已存在的字段指定非空约束,可以使用 ALTER TABLE ... ALTER COLUMN ... SET NOT NULL
语法:
ALTER TABLE 表名
ALTER COLUMN 字段名 SET NOT NULL;
唯一约束
如果要限定某个字段的值是唯一的,可以使用唯一约束 UNIQUE
:
CREATE TABLE 表名(
字段名 数据类型 UNQIUE
);
也可以在表约束中使用:
CREATE TABLE 表名(
字段名 数据类型,
UNIQUE(字段名)
);
表约束中使用的场景更多的是多个字段组成的唯一约束:
主键约束
使用 PRIMARY KEY
给字段加主键约束:
- 每张表可以没有主键约束,但最多只能有一个主键约束
- 主键约束自动具备了非空约束(
NOT NULL
)和唯一约束(UNQIUE
)
你可以在字段后面加上主键约束:
CREATE TABLE 表名(
字段名 数据类型 PRIMARY KEY
);
也可以在表约束里指定:
CREATE TABLE 表名(
字段名 数据类型,
PRIMARY KEY(字段名)
);
对于表约束,更多的是指定复合约束:
CREATE TABLE 表名(
字段1 数据类型,
字段2 数据类型,
PRIMARY KEY(字段1, 字段2)
);
- 主键约束不可能出现
NULL
值。在大部分数据库中,如果你在INSERT
时给一个自动编号的主键设置NULL
,结果是自动编号的值。- 你可以给多个字段同时设置非空和唯一约束
- 虽然主键约束具备非空和唯一约束的特性,但一张表中只能有一个主键约束(一张表中,只能有一个
PRIMARY KEY
约束,哪怕它的行为和NOT NULL
+UNQIUE
类似)
CHECK 约束
PostgreSQL 的 CHECK 约束是非常强大的,对比一下 MySQL 的 CHECK 约束,你会觉得 MySQL 简直弱鸡一只。
CHECK
约束,允许你指定字段中的值是否必须满足特定条件CHECK
约束,在插入或修改值之前,会对值进行评估,如果满足条件则对值进行写入;否则报错
示例:
DROP TABLE IF EXISTS employees;
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
first_name VARCHAR (50),
last_name VARCHAR (50),
birth_date DATE CHECK (birth_date > '1900-01-01'),
joined_date DATE CHECK (joined_date > birth_date),
salary numeric CHECK(salary > 0)
);
这个示例中有三个 CHECK
约束:
birth_date
的约束:限定生日必须在1900-01-01
之后的日期joined_date
的约束:限定入职时间必须大于其生日salary
的约束:限定薪资必须大于0
为已存在的字段加上 CHECK
约束:
ALTER TABLE 表名
ADD CONSTRAINT 字段名
CHECK (条件);
非空约束其实可以看作 CHECK
约束的一种:
外键约束
外键是指引用其它表的主键或唯一字段的值。一张表可以有多个外键。外键有益于维护数据的完整性。
语法:
[CONSTRAINT fk_name]
FOREIGN KEY(fk_columns)
REFERENCES parent_table(parent_key_columns)
[ON DELETE delete_action]
[ON UPDATE update_action]
其中,action
的取值可能是:
SET NULL
:设置为NULL
值 ——前提是,该字段没有NOT NULL
或PRIMARY KEY
约束SET DEFAULT
:设置为默认值RESTRICT
:限制操作NO ACTION
:无任何操作CASCADE
:联动操作
包括阿里在内的诸多互联网巨头都提出不要使用外键约束,而是由程序编码来处理数据,原因:
- 外键约束会增加额外的系统开销
- 某些时候,外键约束会引入
NULL
值,而大部分互联网项目中NULL
值是不被允许- 在分布式系统中,可能根本没办法使用外键约束,因为每个微服务(包括它对应的数据库)都是独立的
特别说明
CREATE TABLE tb1(
c1 INTEGER UNQIUE,
c2 INTEGER UNQIUE,
);
和
CREATE TABLE tb2(
c1 INTEGER ,
c2 INTEGER ,
UNIQUE(c1, c2)
);
含义是不一样的。前者表示 c1
和 c2
分别是唯一约束;后者表示c1
和 c2
一起组成唯一索引。
-- 对于tb1
INSERT INTO tb1(c1,c2) VALUES
(1, 2),
(1, 3), -- 错误
(2, 2); -- 错误
-- 对于tb2
INSERT INTO tb2(c1,c2) VALUES
(1, 2),
(1, 3), -- 正确
(2, 2); -- 正确