404 Not Found

404 Not Found


nginx

约束与键

约束与键

🌍 生活类比

把数据库想象成一个严格的办公室:

约束就是数据库的"规则守卫",在数据写入时自动检查,防止脏数据进入。


🎯 核心概念

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 个

📝 作业

  1. 创建一个 customers 表,包含:主键、非空姓名、唯一邮箱、电话(可选)、注册日期(默认当天)、年龄(18-120)。
  2. 创建一个 orders 表,外键关联 customers,订单金额必须大于 0,状态只能是 pendingpaidshippedcompleted
  3. 为已有的 employees 表添加一个 CHECK 约束,确保工资不低于最低工资标准 2000。
  4. 思考:如果删除 departments 表中某个部门,而 employees 表中还有员工属于该部门,不同的 ON DELETE 策略会产生什么结果?

下一课

下一节我们将通过 实战:多表查询综合 — 综合运用 JOIN、子查询和集合运算解决真实业务场景。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏