约束是保证数据完整性的重要手段。MySQL支持以下几种约束:
主键是唯一标识每行记录的字段,具有唯一性和非空性。例如:
-- 单列主键(使用一列作为主键,通常使用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)主键的选择建议:
业务主键 vs 代理主键
业务主键:使用有业务含义的字段,如身份证号、学号
代理主键:使用无业务含义的自增ID
推荐使用代理主键(自增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)唯一约束确保字段值不重复,但可以为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
主键会自动创建聚簇索引,唯一约束创建非聚簇索引
外键用于建立表与表之间的关系,保证引用完整性。例如:
-- 部门表
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 CASCADESET 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)外键使用的争议:
在实际项目中,是否使用外键约束存在不同观点:
使用外键:保证数据完整性,数据库层面防止脏数据
不使用外键:性能更好(省去外键检查),灵活性更高,在应用层控制关系
我的建议是:小型项目或内部系统可以用外键保证完整性;大型互联网应用通常在应用层维护关系,不用数据库外键。
非空约束确保字段必须有值。例如:
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。
默认值约束可用于在插入数据时没有给字段传值,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)检查约束用于限制字段的取值范围。在 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语法虽然可以写,但会被忽略不生效。
更多知识请阅读后续章节……谢谢!!!