一、主键(Primary Key):表的 "唯一身份证"
主键是表中用于唯一标识每条记录的字段(或字段组合),是关系型数据库数据完整性的基础。
1. 核心特性
- 唯一性:主键值不可重复,确保每条记录都能被唯一识别。
- 非空性 :主键字段自动默认
NOT NULL
,不允许空值。 - 单一性:一个表只能有一个主键(可由多列组合,即 "复合主键")。
- 索引自带:主键会自动创建聚簇索引,大幅提升查询效率。
2. 主要作用
- 数据标识 :在全表或跨表场景中,精准定位某一条记录(如用户表用
id
唯一标识用户)。 - 完整性维护 :阻止重复或无效数据插入 / 更新(如避免用户表出现两个相同
id
的记录)。 - 提升查询性能:基于主键的查询会通过聚簇索引快速定位,避免全表扫描。
- 表间关联基础 :作为外键的引用源,实现多表关联(如订单表的
user_id
引用用户表的id
)。
3. 实战操作
创建主键(建表时)
-- 单字段主键(最常用)
CREATE TABLE users (
id INT AUTO_INCREMENT, -- 自增属性常与主键搭配
username VARCHAR(50) NOT NULL,
password VARCHAR(50) NOT NULL,
PRIMARY KEY (id) -- 声明 id 为主键
);
-- 复合主键(多字段组合,如"学生-课程"关联表)
CREATE TABLE student_course (
student_id INT NOT NULL,
course_id INT NOT NULL,
score INT,
PRIMARY KEY (student_id, course_id) -- 两字段组合为主键
);
修改主键
MySQL 不支持直接修改主键,需先删除旧主键,再创建新主键:
-- 1. 删除旧主键
ALTER TABLE users DROP PRIMARY KEY;
-- 2. 创建新主键(如将 username 设为主键,需确保其唯一非空)
ALTER TABLE users ADD PRIMARY KEY (username);
删除主键
ALTER TABLE users DROP PRIMARY KEY;
二、外键(Foreign Key):表间的 "关联桥梁"
外键是用于关联两个表的字段,确保子表(从表)的记录在父表(主表)中存在对应值,维护多表数据一致性。
1. 核心特性
- 参照完整性 :子表外键值必须是父表主键(或唯一键)的已存在值(或
NULL
,若外键允许空)。 - 级联操作支持:可配置 "级联更新""级联删除",父表数据变更时自动同步子表。
- 依赖主键:外键必须引用父表的主键或唯一键(确保引用字段值唯一)。
2. 主要作用
- 防止孤立数据 :避免子表出现 "无父记录" 的数据(如订单表的
user_id
不能指向不存在的用户)。 - 简化多表操作:通过外键实现多表连接查询(如关联用户表和订单表,查询某用户的所有订单)。
- 规范数据模型:清晰表达表间关系(如 "用户 - 订单" 的一对多关系),减少数据冗余。
3. 实战操作
创建外键(建表时)
-- 父表:users(已存在,主键为 id)
-- 子表:orders(通过 user_id 关联 users 的 id)
CREATE TABLE orders (
order_id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT, -- 外键字段
owner VARCHAR(50) NOT NULL,
level INT NOT NULL,
-- 声明外键:user_id 引用 users 的 id
FOREIGN KEY (user_id) REFERENCES users(id)
-- 可选:级联操作配置
ON UPDATE CASCADE -- 父表 id 更新时,子表 user_id 自动同步
ON DELETE SET NULL -- 父表记录删除时,子表 user_id 设为 NULL(需外键允许空)
);
修改外键
需先删除旧外键,再创建新外键(外键需命名,便于删除):
-- 1. 查看外键名(若未自定义,MySQL 会自动生成)
SHOW CREATE TABLE orders; -- 输出中找"CONSTRAINT 外键名 FOREIGN KEY..."
-- 2. 删除旧外键(假设外键名为 fk_orders_user)
ALTER TABLE orders DROP FOREIGN KEY fk_orders_user;
-- 3. 创建新外键(如关联新的父表 new_users 的 id)
ALTER TABLE orders ADD CONSTRAINT fk_orders_newuser
FOREIGN KEY (user_id) REFERENCES new_users(id);
删除外键
ALTER TABLE orders DROP FOREIGN KEY fk_orders_user;
三、索引(Index):查询的 "加速引擎"
索引是对表中一个或多个字段的值进行排序的特殊数据结构,核心作用是减少数据扫描范围,提升查询速度。
1. 核心特性
- 加速查询:通过索引快速定位数据,避免全表扫描(尤其大数据量场景)。
- 不改变数据:索引是 "数据的副本",不影响表中原始数据的存储。
- 有维护成本:插入 / 更新 / 删除数据时,需同步更新索引,可能降低写操作性能。
2. 索引分类(按常用维度)
分类维度 | 类型 | 特点与应用场景 |
---|---|---|
数据结构 | B+Tree 索引 | 支持范围查询、排序,MySQL 默认索引类型(如主键索引、普通索引) |
Hash 索引 | 仅支持等值查询,速度极快,Memory 引擎支持(如用户登录时按用户名查密码) | |
Fulltext 索引 | 支持文本分词搜索,适用于文章、商品描述等长文本字段(如博客内容关键词查询) | |
物理存储 | 聚簇索引 | 索引与数据存储在一起,表仅一个(通常是主键),范围查询效率高 |
二级索引(辅助索引) | 索引存储聚簇索引值,需 "回表" 查询数据(如普通索引、前缀索引) | |
字段个数 | 单列索引 | 仅对单个字段建索引(如按 age 查学生) |
联合索引(复合索引) | 对多个字段建索引,需遵循 "最左前缀原则"(如按 customer_id+order_date 查订单) |
3. 实战操作
创建索引
-- 1. 普通索引(单列,B+Tree 类型)
CREATE INDEX idx_users_username ON users(username);
-- 2. 联合索引(多列,需注意字段顺序)
CREATE INDEX idx_orders_customer_date ON orders(customer_id, order_date);
-- 3. 前缀索引(对长字段前 N 个字符建索引,减少存储)
CREATE INDEX idx_url_prefix ON urls(url(10)); -- 对 url 前 10 个字符建索引
-- 4. Fulltext 索引(文本搜索)
CREATE FULLTEXT INDEX idx_articles_content ON articles(content);
查看索引
-- 查看表的所有索引
SHOW INDEX FROM users;
-- 或通过信息_schema 查看
SELECT * FROM information_schema.STATISTICS WHERE TABLE_NAME = 'users';
修改索引
MySQL 不支持直接修改索引,需先删除再重建:
-- 1. 删除旧索引
DROP INDEX idx_users_username ON users;
-- 2. 创建新索引(如将前缀长度改为 15)
CREATE INDEX idx_users_username ON users(username(15));
删除索引
DROP INDEX idx_users_username ON users;
强制使用索引
当 MySQL 优化器选择错误索引时,可强制指定索引:
SELECT * FROM users FORCE INDEX (idx_users_username) WHERE username = 'tom';
索引使用追踪(EXPLAIN)
通过 EXPLAIN
分析索引是否被使用:
EXPLAIN SELECT * FROM users WHERE username = 'tom';
关键字段解读:
type
:访问类型(const
最佳,ALL
为全表扫描,需优化);key
:实际使用的索引(NULL
表示未用索引);rows
:估算扫描行数(越少越好)。
四、Check 约束(Check Constraint):数据的 "合法性门卫"
Check 约束用于强制字段值满足特定条件,确保插入 / 更新的数据符合业务规则(MySQL 8.0.16 及以后正式支持)。
1. 核心特性
- 条件验证:仅允许满足约束条件的数据写入(如年龄必须在 18-60 岁)。
- 数据库层控制:无需在应用层重复写验证逻辑,确保多端操作的数据一致性。
- 支持复杂条件:可基于单个字段或多字段组合设置条件(如订单最终金额 = 总金额 × (1 - 折扣率))。
2. 主要作用
- 数据有效性:防止非法数据(如负金额、无效日期)写入数据库。
- 简化应用逻辑:减少应用层代码冗余(如无需在后端、前端分别写年龄验证)。
- 维护数据质量:与主键、外键配合,构建完整的数据完整性体系。
3. 实战操作
创建 Check 约束
-- 1. 建表时添加(单字段条件)
CREATE TABLE employees (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
age INT CHECK (age >= 18 AND age <= 60), -- 年龄约束 18-60 岁
salary DECIMAL(10,2) CHECK (salary > 0) -- 薪资约束为正数
);
-- 2. 建表后添加(多字段条件,需命名约束)
ALTER TABLE orders ADD CONSTRAINT chk_orders_finalamount
CHECK (final_amount = total_amount * (1 - discount_rate));
查看 Check 约束
-- 通过 information_schema 查看表的所有约束
SELECT * FROM information_schema.TABLE_CONSTRAINTS
WHERE TABLE_NAME = 'employees' AND CONSTRAINT_TYPE = 'CHECK';
修改 Check 约束
需先删除旧约束,再创建新约束:
-- 1. 删除旧约束(假设约束名为 chk_employees_age)
ALTER TABLE employees DROP CONSTRAINT chk_employees_age;
-- 2. 创建新约束(如年龄改为 20-65 岁)
ALTER TABLE employees ADD CONSTRAINT chk_employees_age
CHECK (age >= 20 AND age <= 65);
删除 Check 约束
ALTER TABLE employees DROP CONSTRAINT chk_employees_age;
4. 使用注意事项
- 性能影响:复杂约束(如多字段运算)会增加插入 / 更新的耗时,避免过度使用。
- 兼容性:低版本 MySQL(<8.0.16)会忽略 Check 约束,需用触发器替代。
- 合理性:约束条件需符合业务逻辑(如不要设置 "年龄必须为偶数" 这类无意义约束)。
五、存储过程(Stored Procedure):SQL 代码的 "封装容器"
存储过程是预先编译并存储在数据库中的一组 SQL 语句集合,可通过调用名执行,实现业务逻辑封装。
1. 核心特性
- 预编译:创建时编译,调用时无需重复编译,提升执行效率。
- 封装性:将复杂逻辑(如多表更新、数据统计)封装为一个单元,便于复用。
- 支持参数:可接收输入(IN)、输出(OUT)参数,灵活传递数据(如传入用户 ID,返回用户信息)。
2. 核心优点
- 提升性能:减少 SQL 语句网络传输量(仅传调用名和参数),尤其适合复杂逻辑。
- 增强安全性:用户可调用存储过程,但不能直接操作表,防止误删 / 篡改数据。
- 简化维护:业务逻辑变更时,仅需修改存储过程,无需修改所有调用它的应用。
3. 实战操作
创建存储过程
-- 示例:根据用户 ID 查询用户信息(带输入参数)
DELIMITER // -- 修改语句结束符为 //(避免与存储过程内 ; 冲突)
CREATE PROCEDURE sp_get_user_info(IN user_id INT) -- IN 表示输入参数
BEGIN
SELECT id, username, password FROM users WHERE id = user_id;
END //
DELIMITER ; -- 恢复结束符为 ;
-- 示例:统计订单总数(带输出参数)
DELIMITER //
CREATE PROCEDURE sp_count_orders(OUT total INT) -- OUT 表示输出参数
BEGIN
SELECT COUNT(*) INTO total FROM orders; -- 将结果存入输出参数
END //
DELIMITER ;
查看存储过程
-- 1. 查看所有存储过程
SHOW PROCEDURE STATUS\G;
-- 2. 查看指定数据库的存储过程(如 bank 库)
SHOW PROCEDURE STATUS WHERE Db = 'bank'\G;
-- 3. 查看存储过程的创建语句
SHOW CREATE PROCEDURE sp_get_user_info\G;
调用存储过程
-- 1. 调用带输入参数的存储过程
CALL sp_get_user_info(1001);
-- 2. 调用带输出参数的存储过程(需先声明变量接收结果)
SET @order_total = 0;
CALL sp_count_orders(@order_total);
SELECT @order_total AS 订单总数;
修改与删除存储过程
-- 修改:需先删除再重建
DROP PROCEDURE IF EXISTS sp_get_user_info;
-- 重建新逻辑...
-- 删除存储过程
DROP PROCEDURE IF EXISTS sp_get_user_info;
六、触发器(Trigger):表操作的 "自动响应器"
触发器是与表关联的特殊存储过程,当表发生 INSERT
/UPDATE
/DELETE
操作时,自动执行预设逻辑。
1. 核心特性
- 自动触发:无需手动调用,触发事件发生时自动执行。
- 与表绑定:触发器属于某张表,表删除时触发器也随之删除。
- 支持新旧数据 :通过
NEW
(新数据)、OLD
(旧数据)关键字访问操作前后的数据。
2. 触发器类型(按触发时机与事件)
触发时机 | 触发事件 | 作用示例 |
---|---|---|
BEFORE | INSERT | 插入前修改数据(如给用户名自动加前缀)、验证数据合法性 |
AFTER | INSERT | 插入后同步数据(如插入订单后,更新商品库存) |
BEFORE | UPDATE | 更新前校验新值(如薪资不能低于旧值) |
AFTER | UPDATE | 更新后记录日志(如记录用户密码修改时间) |
BEFORE | DELETE | 删除前备份数据(如删除用户前,将数据备份到历史表) |
AFTER | DELETE | 删除后清理关联数据(如删除用户后,删除其所有订单) |
3. 实战操作
创建触发器
-- 示例 1:INSERT 前自动设置订单日期
DELIMITER //
CREATE TRIGGER trg_orders_set_date BEFORE INSERT ON orders
FOR EACH ROW -- 行级触发器,每插入一行执行一次
BEGIN
SET NEW.order_date = CURDATE(); -- NEW 表示待插入的新行
END //
DELIMITER ;
-- 示例 2:DELETE 后自动删除关联的用户记录
DELIMITER //
CREATE TRIGGER trg_orders_delete_user AFTER DELETE ON orders
FOR EACH ROW
BEGIN
DELETE FROM users WHERE id = OLD.user_id; -- OLD 表示待删除的旧行
END //
DELIMITER ;
查看触发器
-- 查看所有触发器(\G 格式化输出,便于阅读)
SHOW TRIGGERS\G;
-- 查看指定表的触发器
SELECT * FROM information_schema.TRIGGERS WHERE TABLE_NAME = 'orders';
修改与删除触发器
-- 修改:需先删除再重建
DROP TRIGGER IF EXISTS trg_orders_set_date;
-- 重建新逻辑...
-- 删除触发器
DROP TRIGGER trg_orders_set_date;
4. 关键概念:NEW
与 OLD
NEW
:仅用于INSERT
/UPDATE
,表示操作后的数据(INSERT
时是新插入的行,UPDATE
时是更新后的行)。OLD
:仅用于UPDATE
/DELETE
,表示操作前的数据(UPDATE
时是更新前的行,DELETE
时是待删除的行)。
七、事务(Transaction):数据操作的 "原子保证"
事务是一组不可分割的 SQL 操作集合,要么全部执行成功(提交),要么全部执行失败(回滚),核心是保证数据一致性。
1. 核心特性(ACID 属性,必须掌握)
- 原子性(Atomicity):事务是 "最小单位",不可拆分(如银行转账,扣款和收款要么都成功,要么都失败)。
- 一致性(Consistency):事务执行前后,数据总状态不变(如转账前 A 有 1000、B 有 500,转账后总和仍为 1500)。
- 隔离性(Isolation):多个事务并发执行时,互不干扰(如事务 1 未提交的修改,事务 2 不可见)。
- 持久性(Durability):事务提交后,修改永久保存(即使数据库崩溃,重启后数据仍存在)。
2. 事务隔离级别(解决并发问题)
MySQL 提供 4 种隔离级别,平衡 "一致性" 与 "并发性能":
隔离级别 | 脏读 | 不可重复读 | 幻读 | 特点与应用场景 |
---|---|---|---|---|
READ UNCOMMITTED | 允许 | 允许 | 允许 | 性能最高,一致性最差(极少用,如临时统计非核心数据) |
READ COMMITTED | 禁止 | 允许 | 允许 | 避免脏读,Oracle 默认级别(如普通业务查询,允许同一事务多次读结果不同) |
REPEATABLE READ | 禁止 | 禁止 | 允许 | 避免脏读、不可重复读,MySQL 默认级别(如订单创建,确保同一事务内数据一致) |
SERIALIZABLE | 禁止 | 禁止 | 禁止 | 一致性最高,性能最差(如金融交易,禁止并发,避免所有问题) |
3. 实战操作(以银行转账为例)
1. 准备环境(创建数据库与表)
-- 创建银行数据库
CREATE DATABASE bank CHARACTER SET utf8;
USE bank;
-- 创建账户表
CREATE TABLE accounts (
account_number INT PRIMARY KEY, -- 账号
account_name VARCHAR(100) NOT NULL, -- 账户名
balance DECIMAL(15,2) NOT NULL -- 余额(保留 2 位小数)
);
-- 插入测试数据(A 账号 1001,余额 1000;B 账号 1002,余额 500)
INSERT INTO accounts VALUES (1001, '张三', 1000.00), (1002, '李四', 500.00);
2. 编写事务脚本(转账 200 元:A 扣 200,B 加 200)
DELIMITER //
CREATE PROCEDURE sp_transfer_funds(
IN source_acc INT, -- 源账号
IN dest_acc INT, -- 目标账号
IN amount DECIMAL(15,2) -- 转账金额
)
BEGIN
DECLARE source_balance DECIMAL(15,2); -- 声明变量存储源账号余额
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK; -- 异常时自动回滚
START TRANSACTION; -- 开始事务
SET autocommit = 0; -- 关闭自动提交(事务内需手动提交)
-- 1. 查询源账号余额
SELECT balance INTO source_balance FROM accounts WHERE account_number = source_acc;
-- 2. 校验余额是否充足,不足则回滚
IF source_balance < amount THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '余额不足,转账失败';
END IF;
-- 3. 源账号扣钱
UPDATE accounts SET balance = balance - amount WHERE account_number = source_acc;
-- 4. 目标账号加钱
UPDATE accounts SET balance = balance + amount WHERE account_number = dest_acc;
COMMIT; -- 所有操作成功,提交事务
SELECT '转账成功' AS result;
END //
DELIMITER ;
3. 调用事务
-- 调用转账存储过程(1001 给 1002 转 200)
CALL sp_transfer_funds(1001, 1002, 200.00);
-- 查看转账结果
SELECT * FROM accounts;
4. 事务控制语句
-- 开始事务
START TRANSACTION; -- 或 BEGIN;
-- 提交事务(所有操作生效)
COMMIT;
-- 回滚事务(撤销所有未提交的操作)
ROLLBACK;
-- 设置隔离级别(会话级,仅当前连接有效)
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
总结
本文梳理的 MySQL 8 七大核心特性,是数据库设计与开发的 "基石":
- 约束类(主键、外键、Check):保障数据完整性,防止非法数据;
- 性能类(索引):提升查询速度,是大数据量场景的 "必备优化手段";
- 逻辑封装类(存储过程、触发器):简化复杂业务,减少应用与数据库的耦合;
- 一致性类(事务):确保关键操作(如转账、订单)的原子性与数据安全。