MySQL 约束的使用

🎉摘要:本文详细讲解MySQL中用于保证数据完整性的六大约束:主键约束(PRIMARY KEY)、唯一约束(UNIQUE)、外键约束(FOREIGN KEY)、非空约束(NOT NULL)、默认值约束(DEFAULT)以及检查约束(CHECK),涵盖其定义、SQL语法、使用示例、选择建议及实际应用中的争议点。

约束是保证数据完整性的重要手段。MySQL支持以下几种约束:

主键约束(PRIMARY KEY)

主键是唯一标识每行记录的字段,具有唯一性和非空性。例如:

-- 单列主键(使用一列作为主键,通常使用ID列)
mysql> CREATE TABLE users1 (
    ->     user_id INT AUTO_INCREMENT PRIMARY KEY, -- 自增
    ->     username VARCHAR(50)
    -> );
Query OK, 0 rows affected (0.01 sec)

-- 单列主键的另一种写法
mysql> CREATE TABLE users2 (
    ->     user_id INT AUTO_INCREMENT, -- 自增
    ->     username VARCHAR(50),
    ->     PRIMARY KEY (user_id)
    -> );
Query OK, 0 rows affected (0.01 sec)

-- 联合主键,使用多列
mysql> CREATE TABLE order_items (
    ->     order_id INT,
    ->     product_id INT,
    ->     quantity INT,
    ->     PRIMARY KEY (order_id, product_id)
    -> );
Query OK, 0 rows affected (0.03 sec)

主键的选择建议:

  1. 业务主键 vs 代理主键

    • 业务主键:使用有业务含义的字段,如身份证号、学号

    • 代理主键:使用无业务含义的自增ID

  1. 推荐使用代理主键(自增ID)

    • 业务主键可能变更(如身份证号纠错),而主键不应变更

    • 自增ID查询效率高,占用空间小

    • 业务主键可以用唯一索引约束

-- 推荐做法
mysql> CREATE TABLE users (
    ->     user_id BIGINT AUTO_INCREMENT PRIMARY KEY,  -- 代理主键
    ->     id_card CHAR(18) UNIQUE,  -- 业务唯一标识用唯一索引
    ->     username VARCHAR(50) UNIQUE
    -> );
Query OK, 0 rows affected (0.03 sec)

唯一约束(UNIQUE)

唯一约束确保字段值不重复,但可以为NULL(NULL不算重复)。例如:

-- 创建数据表
mysql> CREATE TABLE unique_demo (
    ->     id INT AUTO_INCREMENT PRIMARY KEY,
    ->     email VARCHAR(100) UNIQUE, -- 限制邮箱唯一
    ->     phone VARCHAR(20) UNIQUE, -- 限制电话唯一
    ->     username VARCHAR(50),
    ->     UNIQUE KEY uk_username (username)
    -> );
Query OK, 0 rows affected (0.01 sec)

-- 插入重复值会报错
mysql> INSERT INTO unique_demo (email, username) VALUES ('a@example.com', 'user1');
Query OK, 1 row affected (0.00 sec)

-- 报错:email重复
mysql> INSERT INTO unique_demo (email, username) VALUES ('a@example.com', 'user2');
ERROR 1062 (23000): Duplicate entry 'a@example.com' for key 'unique_demo.email'

唯一约束 vs 主键:

  • 一张表只能有一个主键,但可以有多个唯一约束

  • 主键不允许NULL,唯一约束允许NULL

  • 主键会自动创建聚簇索引,唯一约束创建非聚簇索引

外键约束(FOREIGN KEY)

外键用于建立表与表之间的关系,保证引用完整性。例如:

-- 部门表
mysql> CREATE TABLE departments (
    ->     dept_id INT PRIMARY KEY,
    ->     dept_name VARCHAR(50)
    -> );
Query OK, 0 rows affected (0.01 sec)

-- 员工表,带外键
mysql> CREATE TABLE employees (
    ->     emp_id INT PRIMARY KEY,
    ->     emp_name VARCHAR(50),
    ->     dept_id INT, -- 部门ID,该列是 departments 的 ID
    ->     FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
    -> );
Query OK, 0 rows affected (0.01 sec)

外键的级联操作

外键 ON DELETE / ON UPDATE 用来定义父表数据删改时,子表关联数据怎么处理,共 4 种取值:

  • CASCADE 级联删除 / 更新

父记录删 / 改,子表关联记录同步删 / 改。例如:删部门,该部门所有班级一起删掉。

FOREIGN KEY (department_id) REFERENCES departments(department_id) ON DELETE CASCADE
  • SET NULL 置空

父记录删 / 改,子表外键字段设为NULL(该字段必须允许为空)。例如:删除班级,学生的 class_id 变成 null。

  • RESTRICT(默认)/ NO ACTION

禁止操作,只要子表有对应关联数据,父表就不能删 / 改。有班级存在时,无法删除对应部门,防止孤立脏数据。

  • SET DEFAULT

外键设为字段默认值,极少使用,MySQL 基本不支持。

完整实例如下:

mysql> CREATE TABLE employees2 (
    ->     emp_id INT PRIMARY KEY,
    ->     emp_name VARCHAR(50),
    ->     dept_id INT,
    ->     -- 级联删除:部门删除时,所属员工也删除
    ->     FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
    ->         ON DELETE CASCADE
    ->         -- 其他选项:RESTRICT(默认,阻止删除)、SET NULL(设为NULL)
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> CREATE TABLE employees3 (
    ->     emp_id INT PRIMARY KEY,
    ->     emp_name VARCHAR(50),
    ->     dept_id INT,
    ->     -- 级联更新:部门ID变更时,员工表的dept_id同步更新
    ->     FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
    ->         ON UPDATE CASCADE
    -> );
Query OK, 0 rows affected (0.01 sec)

外键使用的争议:

在实际项目中,是否使用外键约束存在不同观点:

  • 使用外键:保证数据完整性,数据库层面防止脏数据

  • 不使用外键:性能更好(省去外键检查),灵活性更高,在应用层控制关系

我的建议是:小型项目或内部系统可以用外键保证完整性;大型互联网应用通常在应用层维护关系,不用数据库外键。

非空约束(NOT NULL)

非空约束确保字段必须有值。例如:

mysql> CREATE TABLE not_null_demo (
    ->     id INT PRIMARY KEY,
    ->     username VARCHAR(50) NOT NULL,
    ->     email VARCHAR(100) NOT NULL,
    ->     phone VARCHAR(20),  -- 可为空
    ->     created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
    -> );
Query OK, 0 rows affected (0.01 sec)

-- 这样插入会报错
mysql> INSERT INTO not_null_demo (id, phone) VALUES (1, '13800138000');  -- 缺少username和email
ERROR 1364 (HY000): Field 'username' doesn't have a default value

-- 正确插入
mysql> INSERT INTO not_null_demo (id, username, email) VALUES (1, 'user1', 'user1@example.com');
Query OK, 1 row affected (0.00 sec)

mysql> select * from not_null_demo;
+----+----------+-------------------+-------+---------------------+
| id | username | email             | phone | created_at          |
+----+----------+-------------------+-------+---------------------+
|  1 | user1    | user1@example.com | NULL  | 2026-07-06 23:08:55 |
+----+----------+-------------------+-------+---------------------+
1 row in set (0.00 sec)

NULL vs 空字符串:

  • NULL:表示 "未知" 或 "不存在"

  • ''(空字符串):表示 "存在但为空"

建议:如果字段必须有值但可以为空,用 NOT NULL DEFAULT '';如果字段可能不存在,可以用 NULL。

默认值约束(DEFAULT)

默认值约束可用于在插入数据时没有给字段传值,mysql 数据库自动填充预设默认值,保障字段必有合理数据。例如:

-- 创建数据库表
mysql> CREATE TABLE default_demo (
    ->     id INT PRIMARY KEY,
    ->     status TINYINT DEFAULT 1 COMMENT '默认启用',
    ->     is_vip TINYINT DEFAULT 0 COMMENT '默认非VIP',
    ->     score INT DEFAULT 0,
    ->     created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    ->     gender CHAR(1) DEFAULT 'U' COMMENT 'U未知,M男,F女'
    -> );
Query OK, 0 rows affected (0.01 sec)

-- 插入时省略有默认值的字段
mysql> INSERT INTO default_demo (id) VALUES (1);
Query OK, 1 row affected (0.00 sec)

mysql> select * from default_demo;
+----+--------+--------+-------+---------------------+--------+
| id | status | is_vip | score | created_at          | gender |
+----+--------+--------+-------+---------------------+--------+
|  1 |      1 |      0 |     0 | 2026-07-07 13:56:55 | U      |
+----+--------+--------+-------+---------------------+--------+
1 row in set (0.00 sec)

检查约束(CHECK,MySQL 8.0.16+)

检查约束用于限制字段的取值范围。在 MySQL5.7 及更早版本忽略 CHECK 约束,不生效,MySQL8.0 完整支持,强制校验字段值。例如:

-- 创建数据表
mysql> CREATE TABLE check_demo (
    ->     id INT PRIMARY KEY,
    ->     age INT CHECK (age >= 0 AND age <= 150),
    ->     gender CHAR(1) CHECK (gender IN ('M', 'F', 'U')),
    ->     email VARCHAR(100),
    ->     CONSTRAINT chk_email CHECK (email LIKE '%@%')
    -> );
Query OK, 0 rows affected (0.01 sec)

-- 违反约束会报错
mysql> INSERT INTO check_demo VALUES (1, 200, 'M', 'test@test.com');  -- age超出范围
ERROR 3819 (HY000): Check constraint 'check_demo_chk_1' is violated.

mysql> INSERT INTO check_demo VALUES (2, 25, 'X', 'test@test.com');   -- gender无效
ERROR 3819 (HY000): Check constraint 'check_demo_chk_2' is violated.

注意:在MySQL 8.0.16之前,CHECK语法虽然可以写,但会被忽略不生效。

更多知识请阅读后续章节……谢谢!!!

  

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号