数据类型是限制数据表中存储数据有效范围的基础手段。然而对于很多应用而言,单纯的数据类型所能提供的约束颗粒度过于宽泛。例如,商品价格字段应当只接受大于零的正数,但标准数据类型中并不存在专门仅限正数的数值类型。另一个关键问题是跨字段或跨行约束:例如在记录商品信息的表中,同一商品编号在全表中必须具备全局唯一性。
为此,SQL 允许在字段列及整张表级别定义约束(Constraints)。约束为你提供了对表内数据合法性的严格管控。一旦有操作试图写入违反约束的数据,系统将立即拒绝并抛出错误中断事务,即便是由默认值生成的数据也必须遵守约束。
检查约束(Check Constraint)是最通用的约束形态,它允许你指定特定列中的数值必须满足一个返回布尔真假值的表达式。例如强制商品价格必须为正数:
CREATE TABLE products (
product_no integer,
name text,
price numeric CHECK (price > 0)
);
检查约束定义紧随数据类型之后,与默认值声明并列(两者的先后顺序完全自由)。检查约束由关键字 CHECK 及其后由圆括号包裹的布尔表达式构成。
你可以为约束显式赋予自定义名称。具名约束在报错时能够使系统异常日志更加直观,并在未来需要修改或删除该约束时方便直接引用:
CREATE TABLE products (
product_no integer,
name text,
price numeric CONSTRAINT positive_price CHECK (price > 0)
);
通过关键字 CONSTRAINT 后接标识符来指定约束名。如果不显式指定,系统会自动生成默认约束名。
检查约束还可以跨多个字段进行联合校验。例如同时存储常规售价和折后价格,并要求折后价必须严格低于原价:
CREATE TABLE products (
product_no integer,
name text,
price numeric CHECK (price > 0),
discounted_price numeric CHECK (discounted_price > 0),
CONSTRAINT valid_discount CHECK (price > discounted_price)
);
当检查约束引用多个列时,它被称为表级约束(table constraint),通常独立写在字段定义列表的末尾。
需要特别关注的是,当检查约束表达式涉及 NULL 空值时,只要表达式的计算结果为真(True)或 NULL,检查约束都视为校验通过。因为按照三值逻辑,NULL 代表“未知”,无法判定其为假。若要强制禁止写入 NULL,必须配合使用非空约束。
非空约束(Not-Null Constraint)严格要求目标列不能接受 NULL 空值:
CREATE TABLE products (
product_no integer NOT NULL,
name text NOT NULL,
price numeric
);
非空约束只能声明为列级约束。非空约束在功能上等价于声明检查约束 CHECK (column_name IS NOT NULL),但在系统底层,原生的 NOT NULL 得到了优化器的深度感知与物理级优化,执行效率显著更高。
唯一约束(Unique Constraint)保证某列或一组列中包含的数据在全表中是唯一的,严禁出现重复记录:
CREATE TABLE products (
product_no integer UNIQUE,
name text,
price numeric
);
作为表级约束,唯一约束可以作用于多个字段列的联合组合:
CREATE TABLE example (
a integer,
b integer,
c integer,
UNIQUE (a, c)
);
这表示在表中不允许出现 a 与 c 同时完全相同的两条数据。
在默认情况下,唯一约束视 NULL 为彼此不同。这意味着在未定义非空约束的前提下,具有唯一约束的列中可以合法存储多个 NULL 空值。
主键约束(Primary Key Constraint)表明一个列或一组列可以作为表中数据行的唯一标识符。在语法和底层机制上,主键等价于 UNIQUE 与 NOT NULL 的组合,但在语义上它确立了整张表的核心数据实体身份。
CREATE TABLE products (
product_no integer PRIMARY KEY,
name text,
price numeric
);
一张表最多只能拥有一个主键约束。主键同样支持由多个列联合构成(复合主键):
CREATE TABLE example (
a integer,
b integer,
c integer,
PRIMARY KEY (a, c)
);
外键约束(Foreign Key Constraint)声明一个列(或一组列)中的取值必须严格匹配另一张表(被称为被引用表或父表)中某行数据的值,用于维护两表之间的引用完整性。
CREATE TABLE orders (
order_id integer PRIMARY KEY,
product_no integer REFERENCES products (product_no),
quantity integer
);
在上述定义中,任何试图在 orders 表中写入未在 products 表中预先存在的 product_no 的操作都将被系统直接拦截。
外键被引用的目标列通常必须是被引用表的主键或具有唯一约束的列。
外键还支持定义级联响应行为。当被引用表中的主键记录被删除或修改时,可以声明由系统自动联动处理子表的外键记录,常见的级联动作包括:
ON DELETE CASCADE:父表记录被删除时,级联物理删除关联的子表数据行。
ON DELETE RESTRICT(或 NO ACTION):只要子表仍有关联引用,直接阻断并拒绝父表的删除操作。
ON DELETE SET NULL:父表记录被删除时,自动将子表对应的外键列置为 NULL。
ON DELETE SET DEFAULT:父表记录被删除时,自动将子表对应的外键列置为该列的默认值。
排除约束(Exclusion Constraint)保证如果将任意两行在指定列上通过指定运算符进行比较,只要其中至少一个运算符返回假(False)或 NULL,则判定为合规。排除约束是唯一约束的通用泛化形态,不仅支持等值比对,还能够原生处理空间几何重叠比对或时间范围重叠比对(例如会议室预订时间区间严禁重叠):
CREATE TABLE room_reservation (
room text,
during tsrange,
EXCLUDE USING gist (room WITH =, during WITH &&)
);
该排除约束确保:在同一会议室(room WITH =)的前提下,严禁插入任何时间范围发生重叠(during WITH &&)的新预订记录。排除约束底层基于索引访问方法(如 GiST 或 SP-GiST)驱动,详见 CREATE TABLE。