核心目标: 学习如何使用约束来强制执行 MySQL 表中数据的完整性和规则,确保数据的准确性、一致性和可靠性。
什么是约束?
约束是在表的数据列上强制执行的规则。它们用于限制可以插入、更新或删除到表中的数据类型或值。如果任何操作违反了约束规则,该操作将被中止。
约束的类型:
NOT NULL
(非空约束)作用:确保列不能存储 NULL
值。如果尝试插入或更新为 NULL
,操作将失败。
语法 (建表时):
column_name data_type NOT NULL
语法 (修改表 - 添加):
ALTER TABLE table_name MODIFY COLUMN column_name data_type NOT NULL;
语法 (修改表 - 移除,允许 NULL):
ALTER TABLE table_name MODIFY COLUMN column_name data_type NULL; -- 或省略 NULL 关键字
示例:
-- 创建表时指定 email 不能为空
CREATE TABLE users (
id INT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL
);
-- 修改现有表的 age 列,使其不能为空
ALTER TABLE users MODIFY COLUMN age INT NOT NULL;
UNIQUE
(唯一约束)作用:保证列(或列组合)中的所有值都是唯一的。允许存在一个 NULL
值(除非该列也被定义为 NOT NULL
)。
语法 (建表时 - 列级):
column_name data_type UNIQUE
语法 (建表时 - 表级,可用于多列):
CONSTRAINT constraint_name UNIQUE (column1, column2, ...)
语法 (修改表 - 添加):
ALTER TABLE table_name ADD CONSTRAINT constraint_name UNIQUE (column_name);
语法 (修改表 - 删除):
ALTER TABLE table_name DROP INDEX constraint_name; -- 唯一约束通常通过唯一索引实现
-- 或 ALTER TABLE table_name DROP CONSTRAINT constraint_name; (如果定义时使用了 CONSTRAINT 关键字)
示例:
-- 创建表时指定 email 唯一
CREATE TABLE employees (
emp_id INT PRIMARY KEY,
emp_name VARCHAR(100),
email VARCHAR(100) UNIQUE
);
-- 创建表时指定用户名和部门 ID 组合唯一
CREATE TABLE memberships (
membership_id INT PRIMARY KEY,
user_id INT,
group_id INT,
CONSTRAINT uq_user_group UNIQUE (user_id, group_id)
);
-- 给 products 表的 product_code 添加唯一约束
ALTER TABLE products ADD CONSTRAINT uq_product_code UNIQUE (product_code);
PRIMARY KEY
(主键约束)作用:唯一标识表中的每一行。它隐含了 NOT NULL
和 UNIQUE
约束。一个表只能有一个主键(可以是单列或多列组合)。主键列的值不能被修改,也不能为 NULL
。
语法 (建表时 - 列级):
column_name data_type PRIMARY KEY
语法 (建表时 - 表级,用于单列或复合主键):
CONSTRAINT constraint_name PRIMARY KEY (column1, column2, ...)
语法 (修改表 - 添加):
ALTER TABLE table_name ADD CONSTRAINT constraint_name PRIMARY KEY (column_name);
语法 (修改表 - 删除):
ALTER TABLE table_name DROP PRIMARY KEY; -- 无需指定名称,因为只有一个
示例:
-- 创建表时将 id 设为主键
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT,
order_date DATE
);
-- 创建表时将 user_id 和 role_id 设为复合主键
CREATE TABLE user_roles (
user_id INT,
role_id INT,
PRIMARY KEY (user_id, role_id)
);
-- 为已存在的表 students 添加主键
-- ALTER TABLE students ADD PRIMARY KEY (student_id);
AUTO_INCREMENT
(自增属性)作用:这不是严格意义上的约束,但常与 PRIMARY KEY
一起用于整数列。当插入新行时,如果未指定该列的值,MySQL 会自动为其生成一个比当前最大值大 1 的唯一序列号。
语法 (建表时):
column_name INT AUTO_INCREMENT PRIMARY KEY
注意:
AUTO_INCREMENT
列。示例:
CREATE TABLE logs (
log_id INT AUTO_INCREMENT PRIMARY KEY,
log_message TEXT,
log_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
5. FOREIGN KEY
(外键约束)
作用:用于在两个表之间建立连接并强制引用完整性。它确保子表(包含外键的表)中的外键列的值必须在父表(被引用的表)的某个主键或唯一键列中存在,或者该外键列的值为 NULL
(如果该列允许 NULL
)。
语法 (建表时 - 表级):
CONSTRAINT constraint_name
FOREIGN KEY (child_column1, ...)
REFERENCES parent_table(parent_column1, ...)
[ON DELETE action]
[ON UPDATE action]
语法 (修改表 - 添加):
ALTER TABLE child_table
ADD CONSTRAINT constraint_name
FOREIGN KEY (child_column)
REFERENCES parent_table(parent_column)
[ON DELETE action]
[ON UPDATE action];
语法 (修改表 - 删除):
ALTER TABLE child_table DROP FOREIGN KEY constraint_name;
ON DELETE
/ ON UPDATE
动作 (指定当父表记录被删除/更新时,子表如何响应):
RESTRICT
(默认): 如果子表存在相关记录,则阻止删除/更新父表记录。CASCADE
: 父表记录删除/更新时,自动删除/更新子表中相关的记录。(级联删除需谨慎!)SET NULL
: 父表记录删除/更新时,将子表中相应的外键列设置为 NULL
。(前提是子表外键列允许 NULL
)。NO ACTION
: 同 RESTRICT
(标准 SQL 的概念,MySQL 中行为类似)。SET DEFAULT
: 父表记录删除/更新时,将子表中相应的外键列设置为其默认值。(不常用,有引擎/版本限制)。示例:
-- 创建 orders 表,其中 customer_id 是外键,引用 customers 表的 id
CREATE TABLE customers (
id INT PRIMARY KEY,
name VARCHAR(100)
);
CREATE TABLE orders (
order_id INT PRIMARY KEY,
order_number VARCHAR(20),
customer_id INT, -- 外键列
order_date DATE,
CONSTRAINT fk_customer
FOREIGN KEY (customer_id) REFERENCES customers(id)
ON DELETE SET NULL -- 如果客户被删除,订单的 customer_id 设为 NULL
ON UPDATE CASCADE -- 如果客户 ID 更新,订单的 customer_id 也更新
);
-- 为已存在的 enrollments 表添加外键到 students 表
-- ALTER TABLE enrollments
-- ADD CONSTRAINT fk_student_enrollment
-- FOREIGN KEY (student_id) REFERENCES students(student_id)
-- ON DELETE RESTRICT; -- 不允许删除有选课记录的学生
DEFAULT
(默认值约束)作用:如果插入新行时没有为某个列指定值,则该列会自动填充指定的默认值。
语法 (建表时):
column_name data_type DEFAULT default_value
语法 (修改表 - 添加/修改):
ALTER TABLE table_name ALTER COLUMN column_name SET DEFAULT default_value;
语法 (修改表 - 删除):
ALTER TABLE table_name ALTER COLUMN column_name DROP DEFAULT;
示例:
-- 创建表时设置注册日期默认为当前日期,状态默认为 'active'
CREATE TABLE accounts (
user_id INT PRIMARY KEY,
username VARCHAR(50),
registration_date DATE DEFAULT (CURDATE()), -- MySQL 8.0.13+ 支持表达式默认值
status VARCHAR(10) DEFAULT 'active'
);
-- 给 products 表的 'is_available' 列设置默认值为 1
ALTER TABLE products ALTER COLUMN is_available SET DEFAULT 1;
CHECK
(检查约束)作用:定义一个布尔表达式,用于验证插入或更新到列中的数据。只有当表达式结果为 TRUE 或 UNKNOWN (由于 NULL) 时,操作才被允许。(MySQL 8.0.16+ 才真正强制执行)
语法 (建表时 - 列级):
column_name data_type CHECK (condition)
语法 (建表时 - 表级):
CONSTRAINT constraint_name CHECK (condition)
语法 (修改表 - 添加):
ALTER TABLE table_name ADD CONSTRAINT constraint_name CHECK (condition);
语法 (修改表 - 删除):
ALTER TABLE table_name DROP CHECK constraint_name;
示例:
-- 创建表时确保价格大于 0
CREATE TABLE products (
product_id INT PRIMARY KEY,
name VARCHAR(100),
price DECIMAL(10, 2) CHECK (price > 0),
stock INT CHECK (stock >= 0)
);
-- 添加约束确保折扣在 0 到 1 之间
ALTER TABLE promotions ADD CONSTRAINT chk_discount CHECK (discount >= 0 AND discount <= 1);
创建一个名为 departments
的表,包含 dept_id
(INT, 主键, 自增) 和 dept_name
(VARCHAR(50), 不能为空, 且唯一)。
答案:
CREATE TABLE departments (
dept_id INT AUTO_INCREMENT PRIMARY KEY,
dept_name VARCHAR(50) NOT NULL UNIQUE
);
创建一个名为 employees
的表,包含 emp_id
(INT, 主键), emp_name
(VARCHAR(100), 不能为空), salary
(DECIMAL(10, 2), 必须大于 0), dept_id
(INT), 并添加一个外键约束 fk_emp_dept
,将 dept_id
关联到 departments
表的 dept_id
列,当部门被删除时,员工的 dept_id
设为 NULL。
答案:
CREATE TABLE employees (
emp_id INT PRIMARY KEY,
emp_name VARCHAR(100) NOT NULL,
salary DECIMAL(10, 2) CHECK (salary > 0),
dept_id INT,
CONSTRAINT fk_emp_dept
FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
ON DELETE SET NULL
);
修改 employees
表,为 salary
列添加一个默认值 3000.00。
答案:
ALTER TABLE employees ALTER COLUMN salary SET DEFAULT 3000.00;
假设 employees
表已存在,并且没有唯一约束。请为 emp_email
列(VARCHAR(100))添加一个唯一约束,约束名为 uq_emp_email
。
答案:
-- 首先确保 emp_email 列存在
-- ALTER TABLE employees ADD COLUMN emp_email VARCHAR(100);
-- 然后添加唯一约束
ALTER TABLE employees ADD CONSTRAINT uq_emp_email UNIQUE (emp_email);
删除 employees
表的外键约束 fk_emp_dept
。
答案:
ALTER TABLE employees DROP FOREIGN KEY fk_emp_dept;
-- 如果不确定约束名,可以先用 SHOW CREATE TABLE employees; 查看