约束与键
约束与键
🌍 生活类比
把数据库想象成一个严格的办公室:
- PRIMARY KEY(主键)= 每个人的工号,绝不重复,绝不为空
- FOREIGN KEY(外键)= 员工必须属于一个真实存在的部门
- UNIQUE(唯一)= 每个人的邮箱不能重复
- NOT NULL(非空)= 姓名栏不能留空
- CHECK(检查)= 年龄必须在 18-65 之间
- DEFAULT(默认值)= 没填状态时,默认为"在职"
约束就是数据库的"规则守卫",在数据写入时自动检查,防止脏数据进入。
🎯 核心概念
PRIMARY KEY — 主键
唯一标识表中每一行,不允许重复,不允许为 NULL。一张表只能有一个主键。
SQL
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
first_name VARCHAR(50) NOT NULL
);
主键也可以由多列组成(复合主键):
SQL
CREATE TABLE order_items (
order_id INT,
product_id INT,
quantity INT,
PRIMARY KEY (order_id, product_id)
);
FOREIGN KEY — 外键
确保一个表中的值必须在另一个表中存在,维护引用完整性。
SQL
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
department_id INT,
FOREIGN KEY (department_id) REFERENCES departments(department_id)
);
外键的级联操作:
| 操作 | 说明 |
|---|---|
ON DELETE CASCADE |
主表删除时,从表相关行也删除 |
ON DELETE SET NULL |
主表删除时,从表外键设为 NULL |
ON DELETE RESTRICT |
有引用时禁止删除(默认) |
ON UPDATE CASCADE |
主表主键更新时,从表外键同步更新 |
SQL
FOREIGN KEY (department_id) REFERENCES departments(department_id)
ON DELETE SET NULL
ON UPDATE CASCADE;
UNIQUE — 唯一约束
确保列中的所有值都不重复,但允许 NULL(大多数数据库中 NULL 可以有多个)。
SQL
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
email VARCHAR(100) UNIQUE
);
UNIQUE vs PRIMARY KEY 的区别:
| 特性 | PRIMARY KEY | UNIQUE |
|---|---|---|
| NULL 值 | ❌ 不允许 | ✅ 允许 |
| 数量 | 每表只能 1 个 | 可以有多个 |
| 作用 | 行的唯一标识 | 列值唯一性保证 |
NOT NULL — 非空约束
确保列不能存储 NULL 值。
SQL
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL
);
CHECK — 检查约束
确保列中的值满足指定条件。
SQL
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
salary DECIMAL(10,2) CHECK (salary > 0),
age INT CHECK (age >= 18 AND age <= 65),
gender CHAR(1) CHECK (gender IN ('M', 'F'))
);
DEFAULT — 默认值
当插入数据未指定该列的值时,自动使用默认值。
SQL
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
status VARCHAR(20) DEFAULT 'active',
hire_date DATE DEFAULT CURRENT_DATE,
salary DECIMAL(10,2) DEFAULT 5000.00
);
约束设计原则
| 原则 | 说明 |
|---|---|
| 最小权限 | 只施加必要的约束,不要过度限制 |
| 就近校验 | 能在列级别定义的就不要用表级别 |
| 命名规范 | 给约束起有意义的名称,便于维护 |
| 业务优先 | 约束应该反映业务规则,而非技术限制 |
📝 基本语法
SQL
-- 创建表时定义约束
CREATE TABLE table_name (
column1 datatype PRIMARY KEY,
column2 datatype NOT NULL,
column3 datatype UNIQUE,
column4 datatype CHECK (condition),
column5 datatype DEFAULT value,
column6 datatype,
FOREIGN KEY (column6) REFERENCES other_table(column)
);
-- 添加约束(已有表)
ALTER TABLE table_name ADD CONSTRAINT constraint_name
UNIQUE (column_name);
ALTER TABLE table_name ADD CONSTRAINT constraint_name
CHECK (condition);
-- 删除约束
ALTER TABLE table_name DROP CONSTRAINT constraint_name;
💡 提示:
- 约束名称在数据库中应唯一,建议格式:
表名_列名_约束类型 NOT NULL只能用ALTER TABLE ... MODIFY添加,不能用ADD CONSTRAINT- 在已有数据的表上添加约束,可能因现有数据不满足条件而失败
📌 示例
示例:创建带约束的员工表
SQL
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
phone VARCHAR(20),
hire_date DATE NOT NULL DEFAULT CURRENT_DATE,
salary DECIMAL(10,2) NOT NULL CHECK (salary > 0),
department_id INT,
CONSTRAINT fk_emp_dept FOREIGN KEY (department_id)
REFERENCES departments(department_id)
ON DELETE SET NULL
);
-- 插入测试数据
INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, salary, department_id)
VALUES (1, '张', '三', 'zhangsan@company.com', '2026-01-15', 8500.00, 1);
-- 以下语句会失败:
-- INSERT ... salary = -100 -- ❌ CHECK 约束
-- INSERT ... first_name NULL -- ❌ NOT NULL 约束
-- INSERT ... email 重复 -- ❌ UNIQUE 约束
示例:为已有表添加和删除约束
SQL
-- 添加 CHECK 约束:工资不超过 500000
ALTER TABLE employees ADD CONSTRAINT chk_salary_max
CHECK (salary <= 500000);
-- 添加 UNIQUE 约束:电话号码唯一
ALTER TABLE employees ADD CONSTRAINT uq_emp_phone
UNIQUE (phone);
-- 删除约束
ALTER TABLE employees DROP CONSTRAINT chk_salary_max;
-- 添加外键约束
ALTER TABLE employees ADD CONSTRAINT fk_emp_dept
FOREIGN KEY (department_id) REFERENCES departments(department_id);
🎬 场景演练
场景一:电商订单系统的约束设计
设计订单表,确保数据完整性。
SQL
-- 产品表
CREATE TABLE products (
product_id INT PRIMARY KEY,
product_name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL CHECK (price > 0),
stock INT NOT NULL DEFAULT 0 CHECK (stock >= 0),
status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'discontinued'))
);
-- 订单表
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT NOT NULL,
order_date DATE NOT NULL DEFAULT CURRENT_DATE,
total_amount DECIMAL(12,2) CHECK (total_amount > 0),
status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'confirmed', 'shipped', 'cancelled')),
CONSTRAINT fk_order_customer FOREIGN KEY (customer_id)
REFERENCES customers(customer_id)
);
-- 订单明细表(复合主键)
CREATE TABLE order_items (
order_id INT,
product_id INT,
quantity INT NOT NULL CHECK (quantity > 0),
unit_price DECIMAL(10,2) NOT NULL CHECK (unit_price > 0),
PRIMARY KEY (order_id, product_id),
FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(product_id)
);
场景二:员工管理系统约束迁移
为已有表补充约束。
SQL
-- 现有表缺少约束,逐步添加
-- 1. 确保邮箱格式正确
ALTER TABLE employees ADD CONSTRAINT chk_email_format
CHECK (email LIKE '%@%.%');
-- 2. 确保入职日期合理
ALTER TABLE employees ADD CONSTRAINT chk_hire_date
CHECK (hire_date >= '2000-01-01' AND hire_date <= CURRENT_DATE);
-- 3. 确保部门外键关系
ALTER TABLE employees ADD CONSTRAINT fk_emp_dept
FOREIGN KEY (department_id) REFERENCES departments(department_id)
ON DELETE SET NULL;
-- 4. 查看表的所有约束(SQL Server)
SELECT name, type_desc
FROM sys.objects
WHERE parent_object_id = OBJECT_ID('employees')
AND type IN ('C', 'F', 'UQ', 'PK');
❓ 常见问题
Q:一张表可以有多个 UNIQUE 约束吗? A: 可以。一张表只能有一个
PRIMARY KEY,但可以有多个UNIQUE约束。例如邮箱和电话都可以分别设置UNIQUE。
Q:外键会降低性能吗? A: 外键在写入时需要额外检查,会略微影响写入性能。但对于大多数应用来说,数据完整性比微小的性能差异更重要。高写入场景可考虑在应用层校验。
Q:CHECK 约束支持子查询吗? A: 大多数数据库的
CHECK约束不支持子查询。如果需要跨表校验,应使用触发器(Trigger)或在应用层实现。
Q:DEFAULT 值和 NOT NULL 有什么关系? A: 如果设置了
NOT NULL但没有DEFAULT,插入时必须显式提供值。如果设置了DEFAULT,插入时可以省略该列,自动使用默认值。两者经常搭配使用。
📖 小节
| 约束 | 作用 | 允许 NULL | 允许多个 |
|---|---|---|---|
| PRIMARY KEY | 唯一标识行 | ❌ | 每表 1 个 |
| FOREIGN KEY | 引用其他表的主键 | ✅ | ✅ |
| UNIQUE | 列值唯一 | ✅ | ✅ |
| NOT NULL | 列不允许为空 | ❌ | ✅ |
| CHECK | 列值满足条件 | 取决于定义 | ✅ |
| DEFAULT | 未指定时的默认值 | - | 每列 1 个 |
- 约束是数据库的"第一道防线",在数据写入时自动校验
- 优先在数据库层面定义约束,而非仅依赖应用层
- 给约束命名,便于后续维护和排错
📝 作业
- 创建一个
customers表,包含:主键、非空姓名、唯一邮箱、电话(可选)、注册日期(默认当天)、年龄(18-120)。 - 创建一个
orders表,外键关联customers,订单金额必须大于 0,状态只能是pending、paid、shipped、completed。 - 为已有的
employees表添加一个CHECK约束,确保工资不低于最低工资标准 2000。 - 思考:如果删除
departments表中某个部门,而employees表中还有员工属于该部门,不同的ON DELETE策略会产生什么结果?
下一课
下一节我们将通过 实战:多表查询综合 — 综合运用 JOIN、子查询和集合运算解决真实业务场景。



