MySQL 从入门到实战
一、数据库基础:先搞懂核心概念
1. 什么是数据库?
数据库(Database)是长期存储在计算机中的结构化数据集合,可以理解为"电子化的文件柜"------用于存储、管理和查询数据,且数据之间存在关联关系。
2. 关系型数据库 vs 非关系型数据库
- 关系型数据库(如 MySQL、Oracle):数据以"表"的形式存储,表与表之间通过"主键""外键"关联,数据结构固定,支持复杂查询(如多表关联、统计分析);
- 非关系型数据库(如 Redis、MongoDB):数据以"键值对""文档"等形式存储,结构灵活,适合高并发、大数据量场景。
3. MySQL 核心术语
| 术语 | 通俗解释 | 示例 |
|---|---|---|
| 数据库(DB) | 存储数据的"仓库",一个数据库包含多个表 | student_db(学生信息数据库) |
| 表(Table) | 数据库中存储数据的核心单元,由行和列组成 | student(学生表) |
| 列(Column) | 表中的"字段",存储同一类数据 | name(姓名列)、age(年龄列) |
| 行(Row) | 表中的"记录",对应一条完整的数据 | (1001,张三,20) |
| 主键(PK) | 表中唯一标识一条记录的列(不可重复、非空) | id(学生编号,唯一标识学生) |
| 外键(FK) | 表中关联其他表主键的列(建立表之间的关系) | class_id(关联班级表的主键) |
| SQL | 操作数据库的语言(结构化查询语言) | SELECT * FROM student |
二、MySQL 安装与环境配置(Windows/Mac 通用)
1. 下载 MySQL
- 官网地址:https://dev.mysql.com/downloads/mysql/
- 版本选择:推荐 8.0 版本(稳定且功能完善),根据操作系统选择对应安装包(Windows 选 MSI,Mac 选 DMG)。
2. 安装关键步骤
Windows 系统
- 运行安装包,选择"Custom"自定义安装,勾选"MySQL Server";
- 配置端口(默认 3306,无需修改);
- 设置 root 账号密码(务必牢记,后续登录用);
- 安装完成后,通过"MySQL 8.0 Command Line Client"启动命令行工具,输入密码登录。
Mac 系统
- 运行 DMG 安装包,按提示完成安装;
- 打开"系统偏好设置"→"MySQL",启动 MySQL 服务;
- 打开终端,输入命令登录:
mysql -u root -p,然后输入安装时设置的密码。
3. 验证安装成功
登录后输入以下命令,若返回 MySQL 版本信息,则安装成功:
sql
SELECT VERSION();
三、MySQL 核心语法:CRUD 操作全解析
CRUD 是数据库操作的核心(Create 新增、Read 查询、Update 修改、Delete 删除),以下所有示例基于「学生表(student)」和「班级表(class)」展开。
1. 数据库操作(创建/查看/删除)
(1)创建数据库
sql
-- 创建数据库(指定字符集为 UTF-8,避免中文乱码)
CREATE DATABASE IF NOT EXISTS student_db
CHARACTER SET utf8mb4
COLLATE utf8mb4_general_ci;
-- 切换到该数据库(后续操作默认在该数据库下执行)
USE student_db;
(2)查看/删除数据库
sql
-- 查看所有数据库
SHOW DATABASES;
-- 查看当前使用的数据库
SELECT DATABASE();
-- 删除数据库(谨慎使用!会删除所有表和数据)
DROP DATABASE IF EXISTS student_db;
2. 表操作(创建/查看/修改/删除)
(1)创建表
先创建「班级表(class)」,再创建「学生表(student)」(通过外键关联班级表):
sql
-- 创建班级表(主键为 class_id)
CREATE TABLE IF NOT EXISTS class (
class_id INT PRIMARY KEY AUTO_INCREMENT, -- 自增主键(自动生成唯一ID)
class_name VARCHAR(50) NOT NULL, -- 班级名称(非空)
grade INT NOT NULL -- 年级(非空)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 创建学生表(外键关联班级表的 class_id)
CREATE TABLE IF NOT EXISTS student (
id INT PRIMARY KEY AUTO_INCREMENT, -- 学生编号(自增主键)
name VARCHAR(50) NOT NULL, -- 姓名(非空)
age INT CHECK (age > 0 AND age < 100), -- 年龄(范围校验:1-99)
gender ENUM('男', '女', '其他') DEFAULT '男', -- 性别(枚举类型,默认男)
class_id INT, -- 关联班级表的主键
create_time DATETIME DEFAULT CURRENT_TIMESTAMP, -- 创建时间(默认当前时间)
-- 外键约束:student.class_id 关联 class.class_id
FOREIGN KEY (class_id) REFERENCES class(class_id)
ON DELETE SET NULL -- 若班级表的记录被删除,学生表的 class_id 设为 NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
(2)查看表结构
sql
-- 查看数据库中所有表
SHOW TABLES;
-- 查看表的字段信息(详细)
DESCRIBE student; -- 或简写 DESC student;
-- 查看表的创建语句
SHOW CREATE TABLE student;
(3)修改表结构(ALTER TABLE)
sql
-- 1. 给学生表添加"手机号"字段
ALTER TABLE student ADD COLUMN phone VARCHAR(20) UNIQUE; -- UNIQUE:手机号唯一
-- 2. 修改"年龄"字段的默认值为 18
ALTER TABLE student ALTER COLUMN age SET DEFAULT 18;
-- 3. 删除"手机号"字段
ALTER TABLE student DROP COLUMN phone;
-- 4. 重命名表(将 student 改为 student_info)
ALTER TABLE student RENAME TO student_info;
(4)删除表
sql
-- 删除表(若存在)
DROP TABLE IF EXISTS student_info;
3. 数据操作(CRUD 核心)
(1)新增数据(INSERT)
sql
-- 1. 给班级表插入数据(指定字段)
INSERT INTO class (class_name, grade)
VALUES ('一年级1班', 1), ('一年级2班', 1), ('二年级1班', 2);
-- 2. 给学生表插入数据(省略字段,按表结构顺序插入)
INSERT INTO student (name, age, gender, class_id)
VALUES
('张三', 20, '男', 1),
('李四', 19, '女', 1),
('王五', 21, '男', 2),
('赵六', 18, '女', 3);
-- 3. 插入部分字段(未指定的字段用默认值)
INSERT INTO student (name, class_id)
VALUES ('孙七', 2); -- age 默认为 18,gender 默认为男
(2)查询数据(SELECT,最常用!)
基础查询
sql
-- 1. 查询学生表所有字段的所有记录(* 表示所有字段,不推荐生产环境使用)
SELECT * FROM student;
-- 2. 查询指定字段(姓名、年龄、班级ID)
SELECT name, age, class_id FROM student;
-- 3. 给字段起别名(简化输出)
SELECT name AS 姓名, age AS 年龄 FROM student;
-- 4. 去重查询(查询所有班级ID,去除重复值)
SELECT DISTINCT class_id FROM student;
条件查询(WHERE)
sql
-- 1. 查询年龄大于 19 的学生
SELECT * FROM student WHERE age > 19;
-- 2. 查询性别为女且班级ID为 1 的学生
SELECT * FROM student WHERE gender = '女' AND class_id = 1;
-- 3. 查询班级ID为 1 或 3 的学生
SELECT * FROM student WHERE class_id IN (1, 3);
-- 4. 查询姓名包含"张"字的学生(模糊查询,% 表示任意字符)
SELECT * FROM student WHERE name LIKE '%张%';
-- 5. 查询年龄为空的学生(IS NULL 判空)
SELECT * FROM student WHERE age IS NULL;
排序查询(ORDER BY)
sql
-- 1. 按年龄升序排序(ASC 可省略,默认升序)
SELECT * FROM student ORDER BY age ASC;
-- 2. 按班级ID降序,年龄升序排序(多字段排序)
SELECT * FROM student ORDER BY class_id DESC, age ASC;
分页查询(LIMIT,避免数据过多)
sql
-- 分页查询:从第 0 条记录开始,查询 2 条(适用于第一页)
SELECT * FROM student LIMIT 0, 2;
-- 简化写法(从第 0 条开始可省略第一个参数)
SELECT * FROM student LIMIT 2;
-- 查询第 2 页数据(每页 2 条:第 3-4 条记录)
SELECT * FROM student LIMIT 2, 2;
聚合查询(COUNT/SUM/AVG/MAX/MIN)
sql
-- 1. 统计学生总数
SELECT COUNT(*) AS 学生总数 FROM student;
-- 2. 统计每个班级的学生人数(GROUP BY 分组)
SELECT class_id AS 班级ID, COUNT(*) AS 学生人数
FROM student
GROUP BY class_id;
-- 3. 计算学生的平均年龄
SELECT AVG(age) AS 平均年龄 FROM student;
-- 4. 查询最大年龄和最小年龄
SELECT MAX(age) AS 最大年龄, MIN(age) AS 最小年龄 FROM student;
多表关联查询(JOIN)
需求:查询学生的姓名、年龄、班级名称(关联 student 表和 class 表)
sql
-- INNER JOIN:只查询两张表中匹配的记录(学生有班级,班级有学生)
SELECT s.name AS 姓名, s.age AS 年龄, c.class_name AS 班级名称
FROM student s -- 给表起别名 s(简化写法)
INNER JOIN class c -- 给表起别名 c
ON s.class_id = c.class_id; -- 关联条件:学生表的 class_id = 班级表的 class_id
-- LEFT JOIN:查询学生表所有记录,即使没有班级(class_name 为 NULL)
SELECT s.name, c.class_name
FROM student s
LEFT JOIN class c
ON s.class_id = c.class_id;
(3)修改数据(UPDATE,谨慎使用!)
sql
-- 修改姓名为"张三"的学生年龄为 22(必须加 WHERE 条件,否则修改所有记录)
UPDATE student
SET age = 22
WHERE name = '张三';
-- 同时修改多个字段(修改李四的性别和班级ID)
UPDATE student
SET gender = '男', class_id = 2
WHERE name = '李四';
(4)删除数据(DELETE,谨慎使用!)
sql
-- 删除姓名为"孙七"的学生(必须加 WHERE 条件,否则删除所有记录)
DELETE FROM student
WHERE name = '孙七';
-- 删除班级ID为 3 的所有学生
DELETE FROM student
WHERE class_id = 3;
4. 约束条件(保证数据完整性)
约束是表设计的核心,用于限制字段的值,避免无效数据插入:

sql
-- 常见约束示例(创建表时添加)
CREATE TABLE IF NOT EXISTS user (
id INT PRIMARY KEY AUTO_INCREMENT, -- 主键约束(唯一、非空)
username VARCHAR(50) NOT NULL UNIQUE, -- 非空约束 + 唯一约束(用户名不能重复)
password VARCHAR(100) NOT NULL, -- 非空约束(密码必填)
email VARCHAR(100) CHECK (email LIKE '%@%'), -- 检查约束(邮箱格式)
status TINYINT DEFAULT 1 -- 默认约束(状态默认1:正常)
);
四、MySQL 实战案例:学生成绩管理系统
需求描述
设计一个简单的学生成绩管理系统,包含 3 张表:
class(班级表):存储班级信息;student(学生表):存储学生基本信息,关联班级表;score(成绩表):存储学生的科目成绩,关联学生表。
1. 创建表结构
sql
-- 1. 班级表
CREATE TABLE IF NOT EXISTS class (
class_id INT PRIMARY KEY AUTO_INCREMENT,
class_name VARCHAR(50) NOT NULL,
grade INT NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 2. 学生表
CREATE TABLE IF NOT EXISTS student (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
age INT DEFAULT 18,
gender ENUM('男', '女', '其他') DEFAULT '男',
class_id INT,
FOREIGN KEY (class_id) REFERENCES class(class_id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 3. 成绩表
CREATE TABLE IF NOT EXISTS score (
score_id INT PRIMARY KEY AUTO_INCREMENT,
student_id INT NOT NULL,
subject VARCHAR(50) NOT NULL, -- 科目
score INT CHECK (score BETWEEN 0 AND 100), -- 成绩(0-100分)
exam_time DATE NOT NULL, -- 考试时间
FOREIGN KEY (student_id) REFERENCES student(id) ON DELETE CASCADE -- 学生删除,成绩也删除
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2. 插入测试数据
sql
-- 插入班级数据
INSERT INTO class (class_name, grade) VALUES ('一年级1班', 1), ('一年级2班', 1);
-- 插入学生数据
INSERT INTO student (name, age, gender, class_id)
VALUES ('张三', 20, '男', 1), ('李四', 19, '女', 1), ('王五', 21, '男', 2);
-- 插入成绩数据
INSERT INTO score (student_id, subject, score, exam_time)
VALUES
(1, '语文', 85, '2024-06-10'),
(1, '数学', 92, '2024-06-10'),
(2, '语文', 78, '2024-06-10'),
(2, '数学', 88, '2024-06-10'),
(3, '语文', 95, '2024-06-10'),
(3, '数学', 80, '2024-06-10');
3. 核心查询场景
(1)查询每个学生的所有科目成绩(关联 3 张表)
sql
SELECT
s.name AS 姓名,
c.class_name AS 班级,
sc.subject AS 科目,
sc.score AS 成绩,
sc.exam_time AS 考试时间
FROM student s
INNER JOIN class c ON s.class_id = c.class_id
INNER JOIN score sc ON s.id = sc.student_id
ORDER BY s.name, sc.subject;
(2)查询每个学生的总分和平均分
sql
SELECT
s.name AS 姓名,
COUNT(sc.subject) AS 考试科目数,
SUM(sc.score) AS 总分,
ROUND(AVG(sc.score), 2) AS 平均分 -- ROUND 保留 2 位小数
FROM student s
LEFT JOIN score sc ON s.id = sc.student_id
GROUP BY s.id, s.name
ORDER BY 总分 DESC;
(3)查询数学成绩大于 85 分的学生信息
sql
SELECT
s.name AS 姓名,
c.class_name AS 班级,
sc.score AS 数学成绩
FROM student s
INNER JOIN class c ON s.class_id = c.class_id
INNER JOIN score sc ON s.id = sc.student_id
WHERE sc.subject = '数学' AND sc.score > 85;
(4)查询每个班级的语文平均分(按班级分组)
sql
SELECT
c.class_name AS 班级,
ROUND(AVG(sc.score), 2) AS 语文平均分
FROM class c
LEFT JOIN student s ON c.class_id = s.class_id
LEFT JOIN score sc ON s.id = sc.student_id AND sc.subject = '语文'
GROUP BY c.class_id, c.class_name;
五、MySQL 优化技巧:让查询更快、更稳定
1. 索引优化(核心!)
索引是提高查询速度的关键,相当于书的"目录",能快速定位数据:
(1)创建索引
sql
-- 给学生表的 name 字段创建普通索引(查询姓名时加速)
CREATE INDEX idx_student_name ON student(name);
-- 给成绩表的 student_id + subject 创建联合索引(多字段查询加速)
CREATE INDEX idx_score_student_subject ON score(student_id, subject);
-- 给经常用于排序的字段创建索引(ORDER BY 加速)
CREATE INDEX idx_student_age ON student(age);
(2)索引使用原则
- 索引不是越多越好:过多索引会减慢插入/修改/删除速度(索引需要维护);
- 适合创建索引的字段:经常用于查询(WHERE)、排序(ORDER BY)、关联(JOIN)的字段;
- 不适合创建索引的字段:数据重复率高(如 gender,只有男/女/其他)、字段长度过长(如 VARCHAR(2000))。
2. SQL 语句优化
(1)避免使用 SELECT *
只查询需要的字段,减少数据传输和内存消耗:
sql
-- 坏示例:查询所有字段
SELECT * FROM student;
-- 好示例:只查询需要的字段
SELECT name, age, class_id FROM student;
(2)避免 WHERE 子句中使用函数或运算
会导致索引失效,查询变慢:
sql
-- 坏示例:对 age 字段做运算,索引失效
SELECT * FROM student WHERE age + 1 = 20;
-- 好示例:改写为字段直接比较
SELECT * FROM student WHERE age = 19;
(3)使用 LIMIT 限制返回数据量
避免一次性查询大量数据,导致内存溢出:
sql
-- 好示例:分页查询前 10 条数据
SELECT * FROM student LIMIT 10;
3. 数据库设计优化
- 表的字段类型尽量小:如年龄用 INT(4字节),不用 BIGINT(8字节);
- 避免使用 NULL 值:NULL 值会增加查询复杂度,可用默认值替代(如 age 默认为 0);
- 分表分库:大数据量场景(如千万级数据),可按时间、地区等维度分表(如 score_202406、score_202407)。
六、MySQL 常见问题与避坑指南
1. 中文乱码问题
- 原因:数据库/表/字段的字符集不是 UTF-8;
- 解决方案:创建数据库时指定字符集为 utf8mb4(支持所有中文和表情符号),如下:
sql
CREATE DATABASE student_db CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
2. 主键自增重复问题
- 原因:手动插入了自增主键的值,导致后续自增ID冲突;
- 解决方案:避免手动插入自增主键,让 MySQL 自动生成;若必须插入,插入后执行
ALTER TABLE student AUTO_INCREMENT = (SELECT MAX(id) + 1 FROM student);重置自增ID。
3. 外键约束导致数据无法删除
- 原因:子表(如 student)关联了父表(如 class)的主键,直接删除父表记录会触发外键约束;
- 解决方案:创建外键时指定
ON DELETE CASCADE(父表记录删除,子表关联记录也删除)或ON DELETE SET NULL(父表记录删除,子表外键设为 NULL)。
4. 索引失效的常见场景
- WHERE 子句中使用
!=、<>、IS NOT NULL; - 使用
LIKE '%xxx'(前缀模糊查询,后缀模糊查询xxx%不会失效); - 字段类型不匹配(如字符串字段用数字查询:
WHERE name = 123); - 联合索引不满足"最左前缀原则"(如联合索引 (a,b,c),查询条件只有 b 和 c,索引失效)。
七、总结:MySQL 学习路径建议
- 基础阶段:掌握数据库/表的创建、CRUD 基础操作,能独立完成简单查询;
- 进阶阶段:深入学习多表关联查询、聚合查询、索引优化,理解约束和事务;
- 实战阶段:结合项目开发(如 Spring Boot + MySQL),解决实际业务场景中的数据存储和查询问题;
- 优化阶段:学习性能调优、分表分库、主从复制等高级特性,应对大数据量、高并发场景。