首页 / MySQL 入门教程 / 其他约束:UNIQUE 与 CHECK

MySQL 入门教程

其他约束:UNIQUE 与 CHECK

本教程共 46 篇 · 第 33 篇 · 更新于 2026-07-30 · 约 10 分钟阅读

MySQLMySQL 入门教程UNIQUECHECKNOT NULLDEFAULT约束数据完整性

33. 其他约束:UNIQUE 与 CHECK

本节目标:掌握 UNIQUE、NOT NULL、DEFAULT、CHECK 四种约束的写法和作用,理解 CHECK 约束的版本差异,学会查看表上的约束信息。

前面讲了主键和外键,这一节把剩下的四种约束一次讲完。它们各自管一摊事,组合起来就能把数据质量卡住。

33.1 UNIQUE 约束

UNIQUE 约束 保证某列(或列组合)的值不重复。和主键的区别是:UNIQUE 允许 NULL,而且一张表可以有多个 UNIQUE。

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) UNIQUE,
    age INT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

email VARCHAR(100) UNIQUE 让邮箱不能重复:

INSERT INTO users(username, email, age) VALUES('zhangsan', 'zhangsan@test.com', 25);
INSERT INTO users(username, email, age) VALUES('lisi', 'zhangsan@test.com', 28);
-- ERROR 1062 (23000): Duplicate entry 'zhangsan@test.com' for key 'email'

UNIQUE 和主键的区别

特性PRIMARY KEYUNIQUE
唯一性
允许 NULL是(可以有多个 NULL)
每张表数量最多 1 个多个
自动建索引聚簇索引二级索引
Note

MySQL 中 UNIQUE 列允许有多个 NULL 值。这是因为 NULL 在 SQL 里代表”未知”,两个未知不算重复。这一点和 SQL Server 等数据库不同。

复合 UNIQUE

多列组合不重复,用 CONSTRAINT ... UNIQUE(列1, 列2)

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100),
    age INT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT uk_username_email UNIQUE(username, email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO users(username, email) VALUES('zhangsan', 'a@test.com');  -- OK
INSERT INTO users(username, email) VALUES('zhangsan', 'b@test.com');  -- OK,email 不同
INSERT INTO users(username, email) VALUES('zhangsan', 'a@test.com');  -- 报错,组合重复

建表后添加和删除 UNIQUE

-- 添加
ALTER TABLE users ADD CONSTRAINT uk_email UNIQUE(email);

-- 删除(需要先查出约束名)
SHOW INDEX FROM users;
ALTER TABLE users DROP INDEX uk_email;
Tip

UNIQUE 约束本质上就是建了一个唯一索引。所以查看和删除 UNIQUE 用的都是索引相关的操作(SHOW INDEXDROP INDEX)。

33.2 NOT NULL 约束

NOT NULL 约束 最简单:这一列不能存 NULL。

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,   -- 必填
    email VARCHAR(100),              -- 可空
    age INT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO users(username, email) VALUES(NULL, 'test@test.com');
-- ERROR 1048 (23000): Column 'username' cannot be null

建表后修改

-- 允许为空
ALTER TABLE users MODIFY username VARCHAR(50) NULL;

-- 改为非空
ALTER TABLE users MODIFY username VARCHAR(50) NOT NULL;
Warning

把已有数据的列改为 NOT NULL 之前,先确认该列没有 NULL 值,否则会报错。可以先执行 UPDATE users SET username = 'unknown' WHERE username IS NULL; 填充默认值。

33.3 DEFAULT 约束

DEFAULT 约束 指定列的默认值。插入数据时如果不给这列赋值,就用默认值。

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100),
    age INT DEFAULT 18,
    status VARCHAR(20) DEFAULT 'active',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 不指定 age 和 status,用默认值
INSERT INTO users(username, email) VALUES('zhangsan', 'zhangsan@test.com');

SELECT age, status, created_at FROM users WHERE username = 'zhangsan';
-- 结果:18 | active | 2026-07-30 12:00:00

常用默认值写法

-- 数值默认值
age INT DEFAULT 0,

-- 字符串默认值
status VARCHAR(20) DEFAULT 'active',

-- 当前时间
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,

-- 更新时自动刷新时间
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP

ON UPDATE CURRENT_TIMESTAMP 是个很实用的写法:每次 UPDATE 这行数据时,updated_at 自动更新为当前时间。

修改默认值

ALTER TABLE users ALTER COLUMN age SET DEFAULT 20;

-- 或者用 MODIFY
ALTER TABLE users MODIFY age INT DEFAULT 20;

-- 删除默认值
ALTER TABLE users ALTER COLUMN age DROP DEFAULT;

33.4 CHECK 约束

CHECK 约束 用来限制列值必须满足某个条件。比如年龄不能为负、订单金额必须大于零。

语法

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100),
    age INT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT chk_age CHECK (age >= 0 AND age <= 150)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO users(username, email, age) VALUES('zhangsan', 'zhangsan@test.com', 25);  -- OK
INSERT INTO users(username, email, age) VALUES('lisi', 'lisi@test.com', -5);
-- ERROR 3819 (HY000): Check constraint 'chk_age' is violated.

也可以写成列级约束:

CREATE TABLE orders (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT,
    amount DECIMAL(10,2) CHECK (amount > 0),
    status VARCHAR(20) DEFAULT 'pending',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

版本差异(重要)

版本CHECK 约束行为
MySQL 5.7 及更早语法能写但不生效,被静默忽略
MySQL 8.0.15语法能写但不生效,被静默忽略
MySQL 8.0.16+真正生效,违反时报错
MySQL 26.7.0生效
Warning

如果你用的 MySQL 版本低于 8.0.16,CHECK 约束写了也白写,数据库不会帮你校验。要么升级到 8.0.16 以上,要么在应用层做校验。本教程基线版本 26.7.0 已完全支持 CHECK 约束。

查看和删除 CHECK 约束

-- 查看
SELECT * FROM information_schema.CHECK_CONSTRAINTS
WHERE TABLE_NAME = 'users';

-- 删除
ALTER TABLE users DROP CHECK chk_age;

33.5 约束查看汇总

想看一张表上所有约束,有几种方式:

-- 查看建表语句(最直观)
SHOW CREATE TABLE users\G

-- 查看索引(主键和UNIQUE)
SHOW INDEX FROM users;

-- 查看 CHECK 约束
SELECT * FROM information_schema.CHECK_CONSTRAINTS WHERE TABLE_NAME = 'users';

-- 查看外键
SELECT * FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_NAME = 'orders' AND REFERENCED_TABLE_NAME IS NOT NULL;

33.6 六大约束速查表

约束关键字作用允许 NULL
主键PRIMARY KEY唯一标识,非空
外键FOREIGN KEY引用另一表,保参照完整性
唯一UNIQUE值不重复
非空NOT NULL不允许 NULL-
默认值DEFAULT不赋值时用默认值-
检查CHECK满足指定条件-

33.7 小结

  • UNIQUE 保证值不重复,允许 NULL,一张表可多个;
  • NOT NULL 禁止 NULL,最简单也最常用;
  • DEFAULT 指定默认值,CURRENT_TIMESTAMP 常用于时间列;
  • CHECK 约束限制列值范围,8.0.16 起才真正生效,低版本被忽略;
  • 六大约束各有侧重,组合使用才能保证数据质量。

下一节进入视图(VIEW),学习如何把复杂查询”存”成一个虚拟表。