PostgreSQL 教程

SQL 数据库约束

约束(Constraints)是确保 PostgreSQL 数据库中数据完整性和一致性的基础。它们就像是数据必须遵守的规则,能够防止不正确、不一致或不完整的信息被存储进来。如果没有约束,数据库很快就会变得不可靠且难以管理。本章将深入探讨四种关键的约束类型:NOT NULLUNIQUEPRIMARY KEYFOREIGN 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_namelast_nameemail 都被定义为 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
);

在这里,usernameemail 都被定义为 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 NULLUNIQUE 的结合体,这意味着主键列不能包含 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_idproducts 表的主键。SERIAL 关键字会自动为该列生成唯一的、递增的整数。

复合主键 (Composite Primary Key):

CREATE TABLE order_items (
    order_id INTEGER,
    product_id INTEGER,
    quantity INTEGER,
    PRIMARY KEY (order_id, product_id)
);

在这里,主键是 order_idproduct_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 表中属于该部门的所有员工也将被一并删除。