mysql数据库的约束

MySQL 约束完全指南

本文档专门讲 约束(Constraint):什么是约束、有哪些种类、怎么在建表或改表时添加,以及常见用法和注意点。每一步都配有详细说明和大量示例,适合零基础新手跟着做。


目录

  1. 什么是约束
  2. 约束有哪些种类
  3. 主键约束(PRIMARY KEY)
  4. 唯一约束(UNIQUE)
  5. 非空约束(NOT NULL)
  6. 默认值(DEFAULT)
  7. 检查约束(CHECK)
  8. 外键约束(FOREIGN KEY)
  9. 约束的添加与删除
  10. 查看约束
  11. 约束命名与最佳实践
  12. 常见错误与注意点
  13. 综合示例与速查表

1. 什么是约束

1.1 定义

约束就是给加的规则,用来限制能存什么样的数据,从而保证数据正确、一致、完整

  • 例如:主键不能重复、不能为空;年龄不能是负数;性别只能是“男”或“女”。
  • 约束由数据库在插入、更新时自动检查:不符合规则的操作会报错,数据就不会被错误写入。

1.2 为什么需要约束

  • 防止脏数据:如年龄写成 -1、重复的学号、空的主键。
  • 保证关系正确:如“订单里的用户 id 必须在用户表里存在”(外键)。
  • 让业务含义更清晰:NOT NULL 表示“这项必填”,UNIQUE 表示“不能重复”。

1.3 约束作用在谁身上

  • 有的约束作用在一列上(如 NOT NULL、UNIQUE、DEFAULT)。
  • 有的作用在一行多列组合上(如 PRIMARY KEY、UNIQUE(列1,列2)、FOREIGN KEY)。

下面按种类逐个讲。


2. 约束有哪些种类

约束类型 关键字 作用简述
主键约束 PRIMARY KEY 唯一标识一行,不能重复、不能为空
唯一约束 UNIQUE 该列(或列组合)的值不能重复
非空约束 NOT NULL 该列必须有值,不能为 NULL
默认值 DEFAULT 插入时不写该列,用默认值
检查约束 CHECK 该列(或行)必须满足一个条件
外键约束 FOREIGN KEY 该列的值必须在另一张表的某列中存在

说明:默认值(DEFAULT)在很多书里不算“约束”,而是“列属性”,但和 NOT NULL 等一起用,习惯上会一起讲,本文也放在一起说明。


3. 主键约束(PRIMARY KEY)

3.1 作用

  • 主键用来唯一标识表中的每一行
  • 要求:不能重复不能为 NULL
  • 一张表最多只能有一个主键,但主键可以由一列多列组合构成。

3.2 单列主键(最常用)

写法一:写在列定义后面

CREATE TABLE users (
    id       INT PRIMARY KEY,
    username VARCHAR(50),
    password VARCHAR(100)
);

写法二:在表末尾单独写(适合多列主键或起名)

CREATE TABLE users (
    id       INT,
    username VARCHAR(50),
    password VARCHAR(100),
    PRIMARY KEY (id)
);

示例:主键 + 自增(最常见)

CREATE TABLE users (
    id       INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50),
    password VARCHAR(100)
);

插入时可以不写 id,数据库会自动从 1 递增。

3.3 复合主键(多列一起做主键)

当“单列”无法唯一标识一行时,可以用多列组合作为主键:

CREATE TABLE score (
    student_id INT,
    subject    VARCHAR(50),
    score      DECIMAL(5,2),
    PRIMARY KEY (student_id, subject)
);

表示:学号 + 科目 一起唯一,即“每个学生每门课只有一条成绩记录”。

3.4 主键与 NULL、重复

  • 主键列不能为 NULL,插入 NULL 会报错。
  • 主键列不能重复,插入或更新成已有值会报错。
-- 错误:主键为 NULL(若 id 是主键且非自增)
INSERT INTO users (id, username) VALUES (NULL, 'test');

-- 错误:主键重复
INSERT INTO users (id, username) VALUES (1, 'a');
INSERT INTO users (id, username) VALUES (1, 'b');  -- 报错:Duplicate entry '1' for key 'PRIMARY'

3.5 主键小结

  • 每张表建议有且仅有一个主键;常用自增整数列 id 作为主键。
  • 主键 = 唯一 + 非空;可单列,可多列(复合主键)。

4. 唯一约束(UNIQUE)

4.1 作用

  • UNIQUE 表示:该列(或列组合)的值不能重复
  • 与主键区别:一张表可以有多个 UNIQUE;UNIQUE 列可以为 NULL(在 MySQL 里多行 NULL 是否算“重复”取决于版本和设置,一般唯一列只允许一个 NULL 或都允许 NULL,视实现而定)。

4.2 单列唯一

示例:用户名不能重复

CREATE TABLE users (
    id       INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) UNIQUE,
    email    VARCHAR(100) UNIQUE,
    password VARCHAR(100)
);

插入重复的 usernameemail 会报错:Duplicate entry 'xxx' for key 'username'

示例:给唯一约束起名(表级写法)

CREATE TABLE users (
    id       INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50),
    email    VARCHAR(100),
    UNIQUE KEY uk_username (username),
    UNIQUE KEY uk_email (email)
);

4.3 多列联合唯一(UNIQUE(列1, 列2))

表示:列1 和 列2 的组合不能重复,单列可以重复。

示例:同一用户对同一商品只能有一条“收藏”记录

CREATE TABLE favorite (
    id         INT PRIMARY KEY AUTO_INCREMENT,
    user_id    INT,
    product_id INT,
    UNIQUE KEY uk_user_product (user_id, product_id)
);
  • user_id=1, product_id=2 只能出现一次;
  • user_id=1, product_id=3 可以再有一条;
  • 即“用户+商品”组合唯一。

4.4 UNIQUE 与 NULL

在 MySQL 中,UNIQUE 列可以存 NULL;多行 NULL 在唯一性上的处理:有的版本允许多个 NULL(把 NULL 视为彼此不相等),有的只允许一个 NULL,需以当前版本为准。若业务上“不允许重复且允许为空”,一般会配合 NOT NULL 或应用层校验。


5. 非空约束(NOT NULL)

5.1 作用

  • NOT NULL 表示:该列必须有值,不能插入或更新为 NULL

5.2 基本用法

CREATE TABLE users (
    id       INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL,
    password VARCHAR(100) NOT NULL,
    nickname VARCHAR(50)           -- 可空,不写 NOT NULL
);

插入时若 usernamepassword 为 NULL,会报错:Column 'username' cannot be null

5.3 与 DEFAULT 配合

“不能为空”又希望有默认值时,可同时写 NOT NULLDEFAULT

CREATE TABLE orders (
    id         INT PRIMARY KEY AUTO_INCREMENT,
    user_id    INT NOT NULL,
    status     VARCHAR(20) NOT NULL DEFAULT '待支付',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

这样 statuscreated_at 不写也会自动填值,且不允许被更新为 NULL。

5.4 空字符串与 NULL

  • NULL 表示“没有值、未知”。
  • 空字符串 '' 是一个值,只是长度为 0。
  • NOT NULL 只禁止 NULL,不禁止空字符串;若业务上“既不能为 NULL 也不能为空串”,可以在应用层检查,或用 CHECK(见下节)限制。

6. 默认值(DEFAULT)

6.1 作用

  • DEFAULT 值:插入时若没有指定该列,就使用默认值;若显式写了值(包括 NULL),则以写的为准。

6.2 基本用法

CREATE TABLE product (
    id        INT PRIMARY KEY AUTO_INCREMENT,
    name      VARCHAR(100) NOT NULL,
    stock     INT DEFAULT 0,
    status    VARCHAR(20) DEFAULT '上架',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

示例插入:

INSERT INTO product (name) VALUES ('手机');
-- stock 为 0,status 为 '上架',created_at 为当前时间

6.3 常用默认值示例

类型 默认值写法示例 说明
数字 DEFAULT 0 数量、状态码等
字符串 DEFAULT '待处理' 状态、类型等
当前时间 DEFAULT CURRENT_TIMESTAMP 创建时间
当前日期 DEFAULT (CURDATE()) 仅日期(MySQL 8.0+)

示例:

created_at DATETIME DEFAULT CURRENT_TIMESTAMP
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP  -- 更新时自动改时间(TIMESTAMP 支持)

6.4 DEFAULT 与 NULL

  • 若列没有写 DEFAULT,且没有 NOT NULL,不写该列时默认就是 NULL
  • 若写了 DEFAULT 值,不写该列时用默认值;若想“允许为空且默认也是空”,可以写 DEFAULT NULL(一般省略不写)。

7. 检查约束(CHECK)

7.1 作用(MySQL 8.0.16+)

  • CHECK 表示:该列(或行)的值必须满足一个条件表达式,不满足则插入/更新报错。
  • MySQL 在 8.0.16 及以后版本才真正支持 CHECK(之前版本会忽略)。

7.2 单列 CHECK

示例:年龄必须在 0 到 150 之间

CREATE TABLE person (
    id   INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50),
    age  INT CHECK (age >= 0 AND age <= 150)
);

或表级写法(可起名):

CREATE TABLE person (
    id   INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50),
    age  INT,
    CONSTRAINT chk_age CHECK (age >= 0 AND age <= 150)
);

示例:性别只能是 男 或 女

CREATE TABLE person (
    id     INT PRIMARY KEY AUTO_INCREMENT,
    name   VARCHAR(50),
    gender CHAR(1) CHECK (gender IN ('男', '女'))
);

示例:成绩 0~100

score DECIMAL(5,2) CHECK (score >= 0 AND score <= 100)

7.3 多列 CHECK(表级)

示例:结束时间必须大于开始时间

CREATE TABLE event (
    id        INT PRIMARY KEY AUTO_INCREMENT,
    start_at  DATETIME,
    end_at    DATETIME,
    CONSTRAINT chk_time CHECK (end_at > start_at)
);

7.4 低版本 MySQL

若使用 8.0.16 之前的版本,CHECK 会被忽略,同类规则需要在应用层触发器里实现。


8. 外键约束(FOREIGN KEY)

8.1 作用与概念

  • 外键表示:本表某列(或列组合)的取值,必须在另一张表主键或唯一键中存在。
  • 用来保证“引用关系”正确,例如:订单表的 user_id 必须在用户表里有对应 id。

术语:

  • 主表(父表):被引用的表(如 users)。
  • 从表(子表):引用别人的表(如 orders)。
  • 外键列:从表中存“主表主键值”的列(如 orders.user_id)。

8.2 基本语法

CREATE TABLE 从表 (
    ...
    外键列 类型,
    FOREIGN KEY (外键列) REFERENCES 主表(主表主键列)
    [ON DELETE 规则] [ON UPDATE 规则]
);

8.3 示例:订单表引用用户表

先建主表:

CREATE TABLE users (
    id       INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL
);

再建从表,并建外键:

CREATE TABLE orders (
    id      INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    amount  DECIMAL(10,2),
    FOREIGN KEY (user_id) REFERENCES users(id)
);
  • orders.user_id 的值必须存在于 users.id 中。
  • 若插入 ordersuser_idusers 里不存在,会报错。
  • 若删除 users 中某行,而 orders 里还有行引用该 id,默认会报错,防止“孤儿数据”。

8.4 ON DELETE 和 ON UPDATE(引用行为)

主表中被引用的行被删除(DELETE)更新(UPDATE)时,从表怎么处理,由 ON DELETE / ON UPDATE 决定。

规则 含义(简述)
RESTRICT 默认;若从表有引用,禁止主表删除/更新
CASCADE 主表删/改时,从表对应行一起删/改
SET NULL 主表删/改时,把从表外键列设为 NULL
NO ACTION 类似 RESTRICT

示例:主表删除用户时,把订单的 user_id 设为 NULL(需允许 user_id 为 NULL)

CREATE TABLE orders (
    id      INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT,                    -- 允许 NULL
    amount  DECIMAL(10,2),
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
);

示例:主表删除用户时,一起删除其订单(级联删除)

FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE

注意:CASCADE 会连带着删/改从表数据,使用时要特别小心。

8.5 外键约束名(可选)

可以给外键起名,方便以后删除或查看:

CREATE TABLE orders (
    id      INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    amount  DECIMAL(10,2),
    CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users(id)
);

8.6 何时用外键

  • 优点:数据库层面保证引用正确,避免“无效的 user_id”等。
  • 缺点:影响插入/更新/删除性能,主从表顺序和迁移更麻烦。
  • 建议:小型项目、对一致性要求高时可用;大型高并发系统有时只在应用层保证关系,不用外键。新手可以先会用语法,再根据项目选择。

9. 约束的添加与删除

9.1 建表时添加(上文已大量示例)

CREATE TABLE 的列定义或表级子句中写 PRIMARY KEY、UNIQUE、NOT NULL、DEFAULT、CHECK、FOREIGN KEY 即可。

9.2 已有表:用 ALTER TABLE 添加

添加主键(表还没有主键时):

ALTER TABLE users ADD PRIMARY KEY (id);

添加唯一:

ALTER TABLE users ADD UNIQUE KEY uk_username (username);

添加非空(列当前没有 NULL 值时才容易成功):

ALTER TABLE users MODIFY COLUMN username VARCHAR(50) NOT NULL;

添加默认值:

ALTER TABLE users MODIFY COLUMN status VARCHAR(20) DEFAULT '正常';

添加检查约束(MySQL 8.0.16+):

ALTER TABLE person ADD CONSTRAINT chk_age CHECK (age >= 0 AND age <= 150);

添加外键:

ALTER TABLE orders ADD CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id);

9.3 删除约束

删除主键:

ALTER TABLE users DROP PRIMARY KEY;

删除唯一约束(通过约束名或索引名):

ALTER TABLE users DROP INDEX uk_username;

删除外键(用约束名):

ALTER TABLE orders DROP FOREIGN KEY fk_orders_user;

取消非空 / 默认值:MODIFY 把列改回可空或去掉 DEFAULT,例如:

ALTER TABLE users MODIFY COLUMN nickname VARCHAR(50) NULL;

删除 CHECK(MySQL 8.0.16+):

ALTER TABLE person DROP CONSTRAINT chk_age;

10. 查看约束

10.1 用 SHOW CREATE TABLE

SHOW CREATE TABLE 表名;

会显示建表语句,其中包含 PRIMARY KEY、UNIQUE、FOREIGN KEY 等定义。

10.2 用 information_schema(了解)

约束信息存在系统库 information_schema 里,例如:

SELECT * FROM information_schema.TABLE_CONSTRAINTS
WHERE TABLE_SCHEMA = '你的数据库名' AND TABLE_NAME = '你的表名';

可查到主键、唯一、外键、CHECK 的约束名和类型。列上的 NOT NULL、DEFAULT 在 COLUMNS 表里。


11. 约束命名与最佳实践

11.1 约束命名(可选但推荐)

  • 主键:常省略名字,或叫 pk_表名
  • 唯一:uk_表名_列名uk_列名
  • 外键:fk_从表_主表fk_列名
  • 检查:chk_表名_列名chk_描述

命名后,删除、查看都会更清晰。

11.2 最佳实践小结

  • 每张表尽量有一个主键,常用自增 id
  • 业务上“不能重复”的列加 UNIQUE(如用户名、邮箱)。
  • 必填列加 NOT NULL;有合理默认值的加 DEFAULT
  • 取值范围固定时可用 CHECK(MySQL 8.0.16+)或 ENUM
  • 外键按需使用,并明确 ON DELETE / ON UPDATE 行为。
  • 加约束前确认现有数据符合约束,否则 ALTER 可能失败(如给已有 NULL 的列加 NOT NULL)。

12. 常见错误与注意点

12.1 主键重复或为 NULL

插入/更新时主键重复或写 NULL(且非自增)会报错,检查 id 是否重复、是否误写 NULL。

12.2 唯一列重复

UNIQUE 列插入已存在的值会报错;若业务允许“重复则更新”,可用 INSERT … ON DUPLICATE KEY UPDATE(非本文重点)。

12.3 外键引用不存在的值

插入从表时,外键列的值必须在主表中存在;删除主表行时,若从表有引用且为 RESTRICT,会报错。

12.4 给已有 NULL 的列加 NOT NULL

先更新数据把 NULL 改成有效值,再 MODIFY … NOT NULL

12.5 CHECK 在低版本不生效

MySQL 8.0.16 之前不支持 CHECK,需用应用层或触发器实现同类逻辑。


13. 综合示例与速查表

13.1 综合建表示例

CREATE TABLE users (
    id         INT PRIMARY KEY AUTO_INCREMENT,
    username   VARCHAR(50)  NOT NULL UNIQUE,
    email      VARCHAR(100) UNIQUE,
    password   VARCHAR(100) NOT NULL,
    status     VARCHAR(20)  NOT NULL DEFAULT '正常',
    created_at DATETIME     DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE orders (
    id      INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    amount  DECIMAL(10,2) NOT NULL CHECK (amount >= 0),
    status  VARCHAR(20)  NOT NULL DEFAULT '待支付',
    CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE RESTRICT
);

13.2 约束速查表

约束 作用 建表示例(单列)
主键 唯一、非空 id INT PRIMARY KEY AUTO_INCREMENT
唯一 不可重复 username VARCHAR(50) UNIQUE
非空 不能为 NULL name VARCHAR(50) NOT NULL
默认值 未指定时用默认 status VARCHAR(20) DEFAULT '正常'
检查 满足条件 age INT CHECK (age>=0 AND age<=150)
外键 引用主表主键 FOREIGN KEY (user_id) REFERENCES users(id)

添加/删除: 主键、唯一、外键、CHECK 用 ALTER TABLE … ADD / DROP;NOT NULL、DEFAULT 用 ALTER TABLE … MODIFY COLUMN …


把约束类型、写法和常见错误过一遍后,你就能在设计表时合理使用主键、唯一、非空、默认、检查和外键,保证数据正确、一致。建议在本地建一两张带各种约束的表,故意插入违反约束的数据,观察报错,加深印象。

发表评论