SQL 数据库约束
约束(Constraints)是确保 PostgreSQL 数据库中数据完整性和一致性的基础。它们就像是数据必须遵守的规则,能够防止不正确、不一致或不完整的信息被存储进来。如果没有约束,数据库很快就会变得不可靠且难以管理。本章将深入探讨四种关键的约束类型:NOT NULL、UNIQUE、PRIMARY KEY 和 FOREIGN KEY,带你全面了解它们的用途、实现方式以及对数据库设计的影响。
1. 了解约束
约束是强制应用在数据表列上的规则。它们限制了可以插入表中的数据类型,从而维护数据的准确性和可靠性。你可以在创建表时定义约束,也可以稍后使用 ALTER TABLE 语句添加约束。约束主要分为两个级别:
- 列级约束 (Column Constraints): 应用于单个列,仅影响该特定列中允许的数据。
- 表级约束 (Table Constraints): 应用于整个表,通常用于涉及多个列的约束条件。
2. NOT NULL 约束 (非空约束)
NOT NULL 约束确保一列不能包含 NULL(空)值。对于那些每一行都必须提供数据的必填列来说,这一点至关重要。
2.1 作用与用法
NOT NULL 约束的主要目的是防止关键列中出现缺失或未定义的值。例如,在客户表中,客户的电子邮件地址可能被认为是必填项,因此应定义为 NOT NULL。
2.2 语法与示例
你可以在创建表时定义 NOT NULL 约束,也可以将其添加到现有表中。
在创建表时使用 NOT NULL:
CREATE TABLE employees (
employee_id SERIAL PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
hire_date DATE,
job_id INTEGER
);在这个例子中,first_name、last_name 和 email 都被定义为 NOT NULL,这意味着每条员工记录都必须为这些列提供具体的值。
向现有表添加 NOT NULL:
ALTER TABLE employees
ALTER COLUMN hire_date SET NOT NULL;这条语句向 hire_date 列添加了 NOT NULL 约束。但是,如果 hire_date 列中已经包含了 NULL 值,这条语句将会失败。在添加约束之前,你必须先用合适的数据更新这些 NULL 值:
-- 将所有空的入职日期更新为当前日期
UPDATE employees SET hire_date = CURRENT_DATE WHERE hire_date IS NULL;更新完 NULL 值后,你就可以成功添加 NOT NULL 约束了。
3. UNIQUE 约束 (唯一约束)
UNIQUE 约束确保一列中的所有值都是互不相同的(唯一的)。虽然一个表可以有多个 UNIQUE 约束,但它只能有一个 PRIMARY KEY(主键)约束。
3.1 作用与用法
UNIQUE 约束用于对不应出现重复值的列强制执行唯一性,但这些列不一定是用于标识一行的主标识符。
3.2 语法与示例
与 NOT NULL 类似,UNIQUE 约束可以在创建表时定义,也可以稍后添加。
在创建表时使用 UNIQUE:
CREATE TABLE users (
user_id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL
);在这里,username 和 email 都被定义为 UNIQUE,这意味着每个用户都必须拥有独一无二的用户名和电子邮件地址。这里还包含了 NOT NULL 约束以防止出现空值,因为默认情况下 UNIQUE 约束是允许存在一个 NULL 值的。
向现有表添加 UNIQUE:
ALTER TABLE users
ADD CONSTRAINT unique_username UNIQUE (username);这将向 username 列添加一个 UNIQUE 约束。你也可以像代码中那样为约束命名(例如 unique_username),以便日后更容易管理。
4. PRIMARY KEY 约束 (主键约束)
PRIMARY KEY 约束唯一地标识表中的每条记录。它实际上是 NOT NULL 和 UNIQUE 的结合体,这意味着主键列不能包含 NULL 值,并且每一行的值都必须是唯一的。一个表只能有一个主键,该主键可以由一个或多个列组成(称为复合主键)。
4.1 作用与用法
PRIMARY KEY 约束是关系型数据库设计的基石。它确保表中的每一行都能被唯一识别,从而允许与其他表建立关联关系。
4.2 语法与示例
在创建表时使用 PRIMARY KEY:
CREATE TABLE products (
product_id SERIAL PRIMARY KEY,
product_name VARCHAR(100) NOT NULL,
price DECIMAL,
description TEXT
);在这个例子中,product_id 是 products 表的主键。SERIAL 关键字会自动为该列生成唯一的、递增的整数。
复合主键 (Composite Primary Key):
CREATE TABLE order_items (
order_id INTEGER,
product_id INTEGER,
quantity INTEGER,
PRIMARY KEY (order_id, product_id)
);在这里,主键是 order_id 和 product_id 的组合,这意味着“订单与产品”的每一次组合都必须是唯一的。
向现有表添加 PRIMARY KEY:
ALTER TABLE products
ADD CONSTRAINT pk_products PRIMARY KEY (product_id);5. FOREIGN KEY 约束 (外键约束)
FOREIGN KEY 约束用于在两个表的数据之间建立链接。它确保一个表(引用表/子表)中的值必须存在于另一个表(被引用表/父表)中。引用表中的外键列指向被引用表中的主键列。这强制执行了参照完整性 (Referential Integrity)。
5.1 作用与用法
FOREIGN KEY 约束对于在关系型数据库的各个表之间建立关系至关重要。它确保了关系的一致性,并防止出现孤立记录(即子表中引用了父表中不存在的记录)。
5.2 语法与示例
在创建表时使用 FOREIGN KEY:
CREATE TABLE departments (
department_id SERIAL PRIMARY KEY,
department_name VARCHAR(50) NOT NULL,
location VARCHAR(50)
);
CREATE TABLE employees (
employee_id SERIAL PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
department_id INTEGER,
FOREIGN KEY (department_id) REFERENCES departments(department_id)
);在这个例子中,employees 表中的 department_id 列是一个外键,它引用了 departments 表中的 department_id 列(主键)。这保证了每个员工都被分配到一个真实存在的部门。
向现有表添加 FOREIGN KEY:
ALTER TABLE employees
ADD CONSTRAINT fk_department
FOREIGN KEY (department_id)
REFERENCES departments(department_id);5.3 ON DELETE 与 ON UPDATE 行为
在定义外键时,你可以指定当被引用的行(父表中的行)被删除或更新时要采取的动作。这些动作有助于维护参照完整性。常见的操作包括:
| 动作 | 描述 |
|---|---|
| ON DELETE CASCADE | (级联删除)当父表中的行被删除时,子表中匹配的行也会被自动删除。 |
| ON UPDATE CASCADE | (级联更新)当父表中的主键值被更新时,子表中匹配的外键值也会被自动更新。 |
| ON DELETE SET NULL | 当父表中的行被删除时,子表中的外键列会被设置为 NULL。这要求子表的外键列允许存放空值。 |
| ON DELETE SET DEFAULT | 当父表中的行被删除时,子表中的外键列会被设置为默认值。这要求该外键列已经定义了默认值。 |
| ON DELETE RESTRICT (或 NO ACTION) | 如果子表中有匹配的行,则阻止删除父表中的行。如果没有指定 ON DELETE 动作,这是默认行为。 |
使用 ON DELETE CASCADE 的示例:
CREATE TABLE employees (
employee_id SERIAL PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
department_id INTEGER,
FOREIGN KEY (department_id) REFERENCES departments(department_id) ON DELETE CASCADE
);如果在 departments 表中删除了某个部门,那么在 employees 表中属于该部门的所有员工也将被一并删除。