原创

MySQL 单表约束超详细教程:主键、非空、唯一、默认值一次讲透

很多人学 MySQL 约束时,第一反应是“这不就是几个关键字吗”。 PRIMARY KEYNOT NULLUNIQUEDEFAULT,语法看一遍就会了。

但真正容易出问题的地方,不在于你会不会写,而在于你到底知不知道它们分别约束了什么、能不能一起用、对插入数据有什么影响、报错时该怎么判断。

单表设计里,这四类约束几乎是最基础的一层防线。表结构如果这一层就松了,后面业务代码写得再认真,脏数据还是会进来。

这篇文章就把 MySQL 单表里最常见的 4 种约束一次讲透:主键、非空、唯一、默认值。内容尽量不绕,重点放在实际建表、插入数据、报错现象和常见误区上。

为什么单表约束一定要学明白

约束的本质不是“限制你写 SQL”,而是“限制错误数据进入表”。

比如下面这些问题,单靠应用层校验并不稳:

  • 用户表允许两个完全相同的用户名
  • 订单表主键为空
  • 手机号字段被插入了 NULL
  • 状态字段没人传值,结果整列都是空的
  • 插入一批数据时,漏传某个字段导致业务状态异常

这些问题只要落库,后面清理成本就比建表时多得多。

所以别把约束当语法题,它本质上是数据质量控制。

先准备一张测试表

为了把四类约束拆开讲,先准备一张示例表。下面这张表故意把几种常见约束都放进去:

CREATE TABLE employee (
    emp_id INT PRIMARY KEY,
    emp_name VARCHAR(20) NOT NULL,
    emp_email VARCHAR(50) UNIQUE,
    emp_salary DECIMAL(10, 2) DEFAULT 0.00,
    emp_status VARCHAR(10) DEFAULT '在职'
);

字段说明:

  • emp_id:员工编号,主键
  • emp_name:员工姓名,不能为空
  • emp_email:邮箱,不能重复
  • emp_salary:工资,默认值 0.00
  • emp_status:员工状态,默认值 '在职'

先看表结构:

DESC employee;

你会看到对应字段的 NullKeyDefault 等信息,这就是约束落地后的直接体现。


1. 主键约束 PRIMARY KEY

主键是表中用于唯一标识一行数据的字段。

说得直白一点: 一张表里,主键列的值不能重复,也不能为 NULL。每一行都必须能靠主键精准找到。

主键的特点

主键约束有两个核心特征:

  • 值唯一
  • 不能为 NULL

这也是为什么很多人会说:主键 = 唯一 + 非空

但它又不只是“唯一非空”这么简单。主键通常承担“记录身份标识”的角色,而普通唯一约束更多只是“这个字段不能重复”。

创建主键的写法

最常见的列级写法:

CREATE TABLE student (
    id INT PRIMARY KEY,
    name VARCHAR(20)
);

也可以用表级写法:

CREATE TABLE student (
    id INT,
    name VARCHAR(20),
    PRIMARY KEY (id)
);

这两种在单字段主键场景下都可以。

主键插入测试

先插入一条正常数据:

INSERT INTO employee (emp_id, emp_name, emp_email, emp_salary, emp_status)
VALUES (1, '张三', 'zhangsan@example.com', 8000.00, '在职');

再插入一条主键重复的数据:

INSERT INTO employee (emp_id, emp_name, emp_email, emp_salary, emp_status)
VALUES (1, '李四', 'lisi@example.com', 9000.00, '在职');

这时会报错,原因很明确:emp_id=1 已经存在了。

再试一下主键为 NULL

INSERT INTO employee (emp_id, emp_name)
VALUES (NULL, '王五');

也会报错。因为主键天然不允许为空。

主键最容易犯的错

误区 1:把有业务意义的字段直接当主键

比如手机号、身份证号、邮箱,看起来都“唯一”,但未必适合做主键。 原因很简单:这些字段虽然可能唯一,但一旦业务规则变化,修改成本很高。

更稳的做法通常是:

  • 用一个独立的 id 作为主键
  • 业务字段再单独加唯一约束

这才是更常见、也更抗变化的设计。

误区 2:认为一张表可以有多个主键

不行。 一张表只能有一个主键。

注意,“一个主键”不等于“只能一个字段”。 如果是联合主键,那本质上也还是一个主键,只不过由多个列组成。

但这篇文章讲的是单表基础约束,单列主键先学透更重要。


2. 非空约束 NOT NULL

NOT NULL 的作用很直接:这个字段必须有值,不能插入 NULL

很多初学者会把“空字符串”和 NULL 混为一谈,这是最常见的坑之一。 它们不是一回事。

  • NULL:表示没有值、未知
  • '':表示空字符串,依然是一个值

NOT NULL 约束禁止的是 NULL,不是空字符串。

创建非空约束的写法

CREATE TABLE customer (
    id INT PRIMARY KEY,
    name VARCHAR(20) NOT NULL,
    phone VARCHAR(20)
);

这里 name 列不能为空,phone 可以为空。

非空约束插入测试

插入正常数据:

INSERT INTO employee (emp_id, emp_name)
VALUES (2, '李四');

插入 NULL

INSERT INTO employee (emp_id, emp_name)
VALUES (3, NULL);

这时会报错,因为 emp_name 设置了 NOT NULL

NOT NULL 和默认值不是一回事

这一点很多人会混。

看这个字段:

emp_status VARCHAR(10) DEFAULT '在职'

它有默认值,但没有写 NOT NULL。 这意味着什么?

意味着:

  • 你插入时不写这个字段,MySQL 会帮你填默认值 '在职'
  • 但你如果明确写了 NULL,能不能成功,要看字段是否允许 NULL

也就是说:

  • DEFAULT 负责“没传时用什么”
  • NOT NULL 负责“能不能是 NULL”

这两个经常一起出现,但控制的是两件不同的事。

非空约束常见误区

误区 1:字段重要就一定要 NOT NULL

不一定。

有些字段天然就是可缺失的,比如:

  • 备注
  • 第二联系方式
  • 离职时间
  • 头像地址

这些字段如果业务上允许暂时没有值,就没必要强行加 NOT NULL。 约束是为业务服务,不是为了把表结构写满。

误区 2:NOT NULL 能防止空内容

防不了。 下面这种写法并不会报错:

INSERT INTO customer (id, name)
VALUES (1, '');

因为 '' 不是 NULL

如果你业务上要求“名字不能为空字符串”,那要靠:

  • 应用层校验
  • 或者更严格的数据库约束设计

单靠 NOT NULL 不够。


3. 唯一约束 UNIQUE

UNIQUE 用来保证字段值不重复。

这类约束非常适合放在“业务上要求唯一”的字段上,比如:

  • 用户名
  • 邮箱
  • 学号
  • 工号
  • 订单编号

创建唯一约束的写法

列级写法:

CREATE TABLE account (
    id INT PRIMARY KEY,
    username VARCHAR(20) UNIQUE,
    password VARCHAR(50) NOT NULL
);

表级写法:

CREATE TABLE account (
    id INT PRIMARY KEY,
    username VARCHAR(20),
    password VARCHAR(50) NOT NULL,
    UNIQUE (username)
);

唯一约束插入测试

先插入一条正常数据:

INSERT INTO employee (emp_id, emp_name, emp_email)
VALUES (4, '王五', 'wangwu@example.com');

再插入相同邮箱:

INSERT INTO employee (emp_id, emp_name, emp_email)
VALUES (5, '赵六', 'wangwu@example.com');

会报错,因为 emp_email 上有唯一约束。

唯一约束和主键有什么区别

这个问题几乎必问。

它们都能保证唯一,但侧重点不一样:

对比项 PRIMARY KEY UNIQUE
是否唯一
是否允许 NULL 一般允许
一张表可定义几个 只能一个 可以多个
主要用途 唯一标识一行记录 保证某字段值不重复

真正实战里,最常见的搭配是:

  • id 做主键
  • usernameemail 做唯一约束

这套设计非常常见,也很稳。

UNIQUENULL 的理解要小心

这是一个很容易被忽略的点。

很多人会想: “唯一约束不允许重复,那多个 NULL 算不算重复?”

在 MySQL 里,通常多个 NULL 是可以存在于唯一列中的,因为 NULL 表示“未知值”,不按普通相等逻辑处理。

比如下面这种情况,往往是允许的:

INSERT INTO employee (emp_id, emp_name, emp_email)
VALUES (6, '小明', NULL);

INSERT INTO employee (emp_id, emp_name, emp_email)
VALUES (7, '小红', NULL);

如果你的业务要求“邮箱不仅不能重复,而且必须填写”,那就不能只写 UNIQUE,而应该写成:

emp_email VARCHAR(50) NOT NULL UNIQUE

这才完整。

唯一约束常见误区

误区 1:有唯一约束就不用主键

不行。

唯一约束和主键虽然都能防重复,但用途不同。 主键是整行记录的身份标识,唯一约束只是字段规则。

别拿 username 直接替代整表主键,后面大概率会难受。

误区 2:唯一字段一定适合做查询主条件

也不绝对。

唯一约束能保证值不重复,但是否适合作为高频查询条件,还要看业务模式和索引设计。 别看到 UNIQUE 就自动等于“这是最优查询列”。那是另外一个话题。


4. 默认约束 DEFAULT

DEFAULT 用于指定字段在未显式赋值时使用的默认值。

这一点很关键: 它只在“你没传这个字段”时生效,不是任何情况下都强制覆盖。

创建默认值的写法

CREATE TABLE orders (
    id INT PRIMARY KEY,
    order_no VARCHAR(30) NOT NULL UNIQUE,
    status VARCHAR(20) DEFAULT '待支付',
    amount DECIMAL(10, 2) DEFAULT 0.00
);

这里:

  • status 默认是 '待支付'
  • amount 默认是 0.00

默认值插入测试

插入时省略带默认值的字段:

INSERT INTO employee (emp_id, emp_name, emp_email)
VALUES (8, '小刚', 'xiaogang@example.com');

这时:

  • emp_salary 会自动取 0.00
  • emp_status 会自动取 '在职'

查询一下:

SELECT * FROM employee WHERE emp_id = 8;

你会看到默认值已经填上了。

如果显式传值,会覆盖默认值

INSERT INTO employee (emp_id, emp_name, emp_email, emp_status)
VALUES (9, '小丽', 'xiaoli@example.com', '离职');

这时 emp_status 就不会再用默认值 '在职',而是使用你传入的 '离职'

DEFAULT 最容易误解的地方

误区 1:默认值等于自动修正错误数据

不是。

DEFAULT 只处理“未提供值”的情况。 如果你明确传了一个值,哪怕这个值不符合业务预期,只要不违反其他约束,数据库照样会收。

误区 2:传 NULL 就会自动变成默认值

不一定。这个得分情况。

如果字段允许 NULL,你显式传了 NULL,数据库通常会按 NULL 处理,而不是帮你改成默认值。

比如:

INSERT INTO employee (emp_id, emp_name, emp_email, emp_status)
VALUES (10, '老周', 'laozhou@example.com', NULL);

这里如果 emp_status 没有 NOT NULL,那它可能就是 NULL,而不是 '在职'

这就是为什么很多字段需要写成:

status VARCHAR(20) NOT NULL DEFAULT '待支付'

少了 NOT NULL,默认值的约束力度其实没你想象中那么强。


5. 四种约束一起用时,怎么理解最清楚

很多人单独看每个约束都懂,一旦组合起来就开始乱。

其实可以这样记:

  • PRIMARY KEY:这行数据是谁
  • NOT NULL:这个字段必须有值
  • UNIQUE:这个字段值不能重复
  • DEFAULT:你不传的时候,我给你补一个默认值

把它们放在一起看,就清楚了。

例如:

CREATE TABLE user_info (
    user_id INT PRIMARY KEY,
    username VARCHAR(20) NOT NULL UNIQUE,
    gender CHAR(1) DEFAULT '男',
    register_status VARCHAR(20) NOT NULL DEFAULT '正常'
);

这个表的约束逻辑是:

  • user_id:必须唯一,不能为空,标识一条用户记录
  • username:必须填写,而且不能重名
  • gender:可以不传,不传就默认 '男'
  • register_status:不传就默认 '正常',但不能是 NULL

这就是组合设计的实际意义: 每个字段根据业务语义,承担不同的数据规则。


6. 修改表时添加约束

有时候表已经建好了,后面才想补约束。这种情况也很常见。

添加非空约束

ALTER TABLE employee
MODIFY emp_name VARCHAR(20) NOT NULL;

添加唯一约束

ALTER TABLE employee
ADD UNIQUE (emp_email);

添加默认值

ALTER TABLE employee
MODIFY emp_status VARCHAR(10) DEFAULT '在职';

添加主键

ALTER TABLE employee
ADD PRIMARY KEY (emp_id);

但这里有个前提: 原表数据必须已经满足约束条件。

比如你想给 emp_email 加唯一约束,如果表里已经有重复邮箱,那这条 SQL 会直接失败。 这不是 MySQL 刁难你,是你历史数据已经脏了。

所以给旧表补约束,先查数据,再改结构,这个顺序别反。


7. 实战里最常见的几种约束设计

下面给几个很典型的字段设计方式。

主键 + 自增

id INT PRIMARY KEY AUTO_INCREMENT

这是最常见的主键设计。

非空 + 唯一

username VARCHAR(30) NOT NULL UNIQUE

表示用户名必填,且不能重复。

非空 + 默认值

status TINYINT NOT NULL DEFAULT 1

表示状态字段必须有值,没传时默认给 1

唯一 + 可空

email VARCHAR(50) UNIQUE

表示邮箱不能重复,但允许暂时不填。

这几种组合,几乎在业务表里天天能见到。


8. 一张表里到底该怎么选约束

可以按下面这个思路判断:

哪些字段适合主键

  • 能唯一标识一条记录
  • 最好稳定,不轻易修改
  • 通常选整数型主键更常见

哪些字段适合非空

  • 业务上必须填写
  • 缺失后会导致记录失去意义
  • 后续逻辑强依赖该值存在

哪些字段适合唯一

  • 业务规则要求不能重复
  • 重复后会造成身份冲突或数据歧义

哪些字段适合默认值

  • 大量数据插入时经常省略
  • 存在合理初始值
  • 不想每次插入都手写一遍

重点不是把所有字段都加满约束,而是让约束和业务含义对得上。


9. 常见面试题顺手总结

主键和唯一有什么区别

  • 主键不能为 NULL,唯一约束一般可以
  • 一张表只能有一个主键,但可以有多个唯一约束
  • 主键主要用于标识记录,唯一约束主要用于限制字段不重复

NOT NULLDEFAULT 有什么区别

  • NOT NULL:不允许字段为 NULL
  • DEFAULT:插入时如果没传值,就使用默认值

两者控制的不是一件事。

UNIQUE 能不能和 NOT NULL 一起用

可以,而且很常见。

username VARCHAR(20) NOT NULL UNIQUE

这表示用户名必须填写,且不能重复。

主键能不能重复、能不能为空

都不行。


10. 最后一段经验话

约束这块最忌讳的不是不会写,而是“看起来会,其实没分清边界”。

尤其是这几个点:

  • 主键不是普通唯一字段
  • NOT NULL 不等于禁止空字符串
  • DEFAULT 不是万能兜底
  • UNIQUE 遇到 NULL 时要特别小心
  • 给旧表加约束前,先检查历史数据

很多数据库问题不是复杂 SQL 搞出来的,而是表设计一开始就太松。 单表约束看起来基础,实际上就是数据质量的第一道门槛。

把这四个约束吃透,后面学外键、索引、表关系设计,思路会顺很多。

正文到此结束
评论插件初始化中...
Loading...