MySQL 从入门到实战

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 系统
  1. 运行安装包,选择"Custom"自定义安装,勾选"MySQL Server";
  2. 配置端口(默认 3306,无需修改);
  3. 设置 root 账号密码(务必牢记,后续登录用);
  4. 安装完成后,通过"MySQL 8.0 Command Line Client"启动命令行工具,输入密码登录。
Mac 系统
  1. 运行 DMG 安装包,按提示完成安装;
  2. 打开"系统偏好设置"→"MySQL",启动 MySQL 服务;
  3. 打开终端,输入命令登录: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 张表:

  1. class(班级表):存储班级信息;
  2. student(学生表):存储学生基本信息,关联班级表;
  3. 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 学习路径建议

  1. 基础阶段:掌握数据库/表的创建、CRUD 基础操作,能独立完成简单查询;
  2. 进阶阶段:深入学习多表关联查询、聚合查询、索引优化,理解约束和事务;
  3. 实战阶段:结合项目开发(如 Spring Boot + MySQL),解决实际业务场景中的数据存储和查询问题;
  4. 优化阶段:学习性能调优、分表分库、主从复制等高级特性,应对大数据量、高并发场景。
相关推荐
有想法的py工程师18 分钟前
PostgreSQL + Debezium CDC 踩坑总结
数据库·postgresql
Nandeska32 分钟前
2、数据库的索引与底层数据结构
数据结构·数据库
小卒过河01041 小时前
使用apache nifi 从数据库文件表路径拉取远程文件至远程服务器目的地址
运维·服务器·数据库
过期动态1 小时前
JDBC高级篇:优化、封装与事务全流程指南
android·java·开发语言·数据库·python·mysql
Mr.朱鹏1 小时前
SQL深度分页问题案例实战
java·数据库·spring boot·sql·spring·spring cloud·kafka
一位代码1 小时前
mysql | 常见日期函数使用及格式转换方法
数据库·mysql
SelectDB1 小时前
Apache Doris 4.0.2 版本正式发布
数据库·人工智能
杰克尼2 小时前
mysql_day01
数据库·mysql
ccino .2 小时前
sql注入中过滤分隔符的测试方法
数据库·sql