MySQL数据库系统学习笔记

一、数据库基础概念

1. 核心组件

**数据库(DB)**:存储数据的文件系统
DBMS:数据库管理系统软件(如MySQL)
SQL:结构化查询语言(操作数据库的标准)

2. 数据库分类

关系型:MySQL/Oracle/SQL Server(二维表结构)
非关系型:Redis/HBase(键值对/文档型)

二、MySQL安装与登录

1. 命令行登录

1
2
3
mysql -uroot -proot
# -u 用户名
# -p 密码

三、SQL语言分类

D:定义
M:操作
Q:查询
C:控制

这部分很重要,重点记忆

DDL:数据定义语言,用来操作数据库对象(数据库,表,字段)

DML:数据操作语言,用来对数据库中表的数据进行增删改

DQL:数据查询语言,用来对数据库中表的数据进行查询操作

DCL:数据控制语言,用来创建数据库用户,控制访问权限

字符库意义对照表

  • SHOW[显示]
  • SELECT[查询]
  • DATABASE[数据库]
  • IF[如果]
  • NOT[没有]
  • EXISTS[存在]
  • DEFAULT[默认]
  • CHARSET[字符集]
  • UTF8[支持中文(万国码)]
  • DROP[删除]
  • IF EXISTS[如果存在]
  • TABLES[表]
  • DESC[描述]
  • CREATE[创建]
  • ALTER[修改]
  • ADD[添加]
  • TRUNCATE[截断,清除]

DDL部分(数据对象操作,数据库操作,表操作,字段操作)

数据定义语言,用来操作数据库对象(数据库,表,字段)

1. DDL(数据定义)

查看所有数据库

1
2
3
-- 查看所有数据库
-- show[显示] databases[所有数据库]
SHOW DATABASES;

查看当前数据库

1
2
3
-- 查看当前数据库
-- select[查询] DATABASE[数据库]
SELECT DATABASE();

创建数据库

1
2
3
4
5
6
7
8
9
10
11
12
-- 创建数据库
-- 在同一个数据库服务器中,不能创建两个名字相同的数据库
-- create[创建] database[数据库] db[数据库名]
-- if[如果] not[没有] exists[存在]
CREATE DATABASE IF NOT EXISTS shop


-- 创建数据库指定字符集
-- default[默认] charset[字符集] utf8[支持中文]
DEFAULT CHARSET utf8;

create database if not exists db3 default charset utf8;

删除数据库

1
2
3
-- 删除数据库
drop[删除] if exists[如果存在]
DROP DATABASE IF EXISTS test;

切换数据库

1
2
select database();
use db;

2. 表操作DDL

查看所有表

1
2
3
-- 查看所有表
-- SHOW[显示] tables[所有表]
SHOW TABLES;

查看指定表的结构

1
2
3
4
-- 查看指定表的结构
-- 查看表的字段,字段的类型,是否可以为null,是否有默认值
-- desc[描述] studentinfo[表名]
desc studentinfo;

查看指定表的建表语句

1
2
3
-- 查看指定表的建表语句
-- show[显示] create[创建] tables[表] studentinfo[表名]
show create table studentinfo;

3. 数据类型详解

数值类型

  • int整数
  • float 小数(单精度)
  • double 小数(双精度)
1
2
3
INT         -- 整数
FLOAT(7,2) -- 单精度浮点
DECIMAL(10,2) -- 精确小数

字符串类型

1
2
3
CHAR(11)    -- 固定长度(适合身份证号)
VARCHAR(50) -- 可变长度(适合姓名)
TEXT -- 大文本
  • char(需要指定长度)固定字符串
  • varchar(需要指定长度)可变字符串

存在面试题,见下

时间类型

1
2
3
4
DATE        -- 日期(YYYY-MM-DD)
TIME -- 时间(HH:MM:SS)
DATETIME -- 日期时间
TIMESTAMP -- 时间戳

4. 完整表示例和表操作

1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE employees (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(30) NOT NULL,
gender CHAR(1) CHECK(gender IN('男','女')),
age INT DEFAULT 18,
salary DECIMAL(10,2),
hire_date DATE,
department_id INT,
CONSTRAINT fk_dept FOREIGN KEY(department_id)
REFERENCES departments(id)
);

修改表

1
2
3
4
-- 修改表
-- 添加一个新的字段 家庭住址 address
-- alter[修改] table[表] emp[表名] add[添加] address[添加的字段名]
alter table emp add address varchar(50);

删除字段

1
2
3
-- 删除字段
-- alter[修改] table[表] emp[表名] drop[删除] address[删除的字段名]
alter table emp drop address;

两种删除表

1
2
3
4
5
6
-- 删除表
drop table if exists emp;

-- 删除表, 并重新创建表
-- truncate[截断,清除]
truncate table emp;

此处存在面试题,见下

四、DML数据操作

DML数据操作语言,用于对数据库中的数据进行增删改操作。

  • 添加数据INSERT
  • 修改数据UPDATE
  • 删除数据DELETE

1. 添加数据

1
2
3
4
5
6
7
8
9
10
11
-- 给指定字段添加数据
insert into emp(eid,enumber,name,sex,age,sal,card,hiredate)
values(1,'1001','jack','男',21,3500,'4311','2025-5-1');

-- 给全部字段添加数据
insert into emp values(2,'1002','tom','男',21,2500,'4311','2025-5-1');

-- 批量添加数据
insert into emp values(3,'1003','lucy','女',20,2500,'4311','2025-5-11'),
(4,'1004','rose','女',19,1500,'4312','2025-5-13'),
(5,'1005','piter','男',22,2300,'4313','2025-5-15');

2. 修改数据

更新类别

1
2
3
4
5
-- 条件更新
UPDATE employees SET salary = salary*1.1 WHERE department_id = 2;

-- 全表更新
UPDATE employees SET age = age + 1;

修改数据

1
2
3
4
5
-- 修改数据  
-- 修改id为1的数据,将name修改为中文杰克
-- where 用于指定条件
update emp set name='JACK',age=22 where eid=1;
-- 修改语句的条件可以有,也可以没有,如果没有指定条件,则修改整张表的数据。

3. 删除数据

1
2
3
4
5
6
-- 条件删除
DELETE FROM employees
WHERE id = 5;

-- 清空表
TRUNCATE TABLE temp_employees;

详细案例

1
2
3
-- 删除数据
delete from emp where eid=1;
-- 删除语句的条件可以有,也可以没有,如果没有指定条件,则删除整张表的数据。

五、DQL数据查询

DQL数据查询语言,用来查询数据库中表的数据

语法结构顺序(强制性顺序,元素可以不存在,但是必须按照这个顺序)

1
2
3
4
5
6
7
select  字段列表
from 表名列表
where 条件列表
group by 分组字段列表
having 分组后条件列表
order by 排序字段列表
limit 分页参数
  • 基本查询(不带条件)
  • 条件查询(where)
  • 聚合函数(count、max、min、avg、sum)
  • 分组查询(group by)
  • 排序查询(order by)
  • 分页查询(limit)

1. 基础查询

在基本查询的DQL语句中,不带任何的查询条件。

1
2
3
4
5
6
7
8
9
10
11
-- 查询指定列
SELECT name, salary FROM employees;

-- 别名查询
SELECT name AS '姓名',
salary*12 '年薪'
FROM employees;

-- 去重查询
SELECT DISTINCT department_id
FROM employees;

实战对照

1
2
3
4
5
6
7
8
9
10
11
-- 查询多个字段
-- 职员姓名,职员性别,职员工资,入职时间
-- select[查询] 字段列表 from[来自] emp[查询的表名]
select name,sex,sal,hiredate from emp;
-- 查询所有字段 * 代表查询所有字段
select * from emp;
-- 字段取别名 as 取别名 as可以省略
select sal '工资',hiredate '入职时间' from emp;
-- 查询员工的年龄有哪几种
-- distinct[去重复]
select distinct age from emp;

2. 条件查询

  • 比较运算符 > < >= <= <>或 != between..and in
  • 逻辑运算符 or 或者 and 并且 not ! 非 不是
1
2
3
4
5
6
7
8
9
10
11
-- 比较查询
SELECT * FROM products
WHERE price > 100 AND stock < 50;

-- 范围查询
SELECT * FROM orders
WHERE order_date BETWEEN '2023-01-01' AND '2023-03-31';

-- 模糊查询
SELECT * FROM customers
WHERE phone LIKE '138%';

实战对照

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 查询年龄大于18岁的  
select * from emp where age>19;
-- 查询年龄小于20岁的
select * from emp where age<20;
-- 查询没有身份证号的员工信息
select * from emp where card is null;
-- 查询有身份证的员工信息
select * from emp where card is not null;
-- 查询年龄在19(包括)和20(包括)岁之间的职员信息
select * from emp where age>=19 && age<=20;
-- BETWEEN...and.. 在某个范围之间(最小值 and 最大值)
select * from emp where age between 19 and 20;

-- 查询性别为女,年龄小于20岁的职员信息
select * from emp where sex='女' and age<20;

-- 查询eid等于 2或者3或者6的员工信息
select * from emp where eid=2 or eid=3 or eid=6;
-- in 在in之后列表中的值 多选
select * from emp where eid in(2,3,6);

**like模糊查询模糊查询通过通配符实现字符串匹配 **
% 表示任意长度的字符 1个或1个以上
_ 表示单个字符

实战对照

1
2
3
4
5
6
7
8
9
10
11
-- 查询姓名为4个字的员工信息
select * from emp where name like '____';

-- 查询姓名为l开头的4个字的员工信息
select * from emp where name like 'l___';

-- 查询名字包含o的员工信息
select * from emp where name like '%o%';

-- 查询名字为l开头的员工信息
select * from emp where name like 'l%';

3. 聚合函数

可以理解为数学函数逻辑

  • COUNT 统计数量
  • AVG 求平均
  • MAX 最大值
  • MIN 最小值
  • SUM 求和
1
2
3
4
5
6
7
8
9
10
11
12
-- 统计员工总数量
SELECT COUNT(*) FROM emp;
-- 统计的是card不为null的数据
SELECT COUNT(card) FROM emp;
-- 求员工的平均年龄
SELECT AVG(age) FROM emp;
-- 求员工的最大年龄
SELECT MAX(age) FROM emp;
-- 求员工的最小年龄
SELECT MIN(age) FROM emp;
-- 求员工的年龄综合
SELECT SUM(age) FROM emp;

4. 分组查询

按照分组阶段将结果相同的内容作为一组,分组的目的是为了统计,所以一般情况会跟聚合函数一起使用。

1
2
3
4
5
6
7
8
9
10
11
12
-- 按照性别分组,统计男女职员各多少人
select sex,count(*) from emp group by sex;

-- 按照性别分组,统计男女员工的平均年龄
select sex,avg(age) from emp group by sex;

-- 按照性别分组,统计男女职员各多少人,小于2的不显示
-- having 筛选
select sex,count(*) from emp group by sex having count(*)>2;

-- 查询年龄大于20岁,按性别分组,统计每组的人数,大于2显示
select sex,count(*) from emp where age>20 group by sex having count(*)>2;

5. 排序查询

ORDER BY 语句对查询结果进行排序。

表达式后面可选 asc(升序)或者 desc(降序)来指定排序方向,如果没有指定方向,默认为asc(升序)

1
2
3
4
5
--   按照年龄对员工进行升序
SELECT * FROM emp ORDER BY age ASC;

-- 按照工资降序排序,按照名字排序(多字段排序)
SELECT * FROM emp ORDER BY sal DESC,NAME;

###6.分页查询limit
用来限制select查询返回的行会,常用于分页,或提取部分数据,提供查询效率。
基本上所有的分页设计都是为了降低服务器负担,提高效率

1
2
--  0 开始索引, 查询3条  
select * from emp limit 0,3;

注意:索引是按照提交数量定的,哪怕没提交成功报错了,但是执行了一次查询,索引仍然会增加
解释:若是0,3,意思是从索引0开始,每次查询三条,若是成功查询,索引会变为3,哪怕出现报错查询则变为1

###查询练习实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 查询年龄在20,21,23的员工信息
SELECT * FROM emp WHERE age IN(20,21,23);

-- 查询性别为男,年龄在20-22岁以内,名字为4个字的员工
SELECT * FROM emp WHERE sex='男' AND (age BETWEEN 20 AND 22) AND NAME LIKE '____';

-- 统计员工表中,年龄小于25岁的,男女员工各多少人
SELECT sex,COUNT(*) FROM emp WHERE age<25 GROUP BY sex;

-- 查询所有年龄小于25岁的员工姓名和年龄,按照年龄升序,如果年龄相同,按照入职时间降序
SELECT NAME,age FROM emp WHERE age<25 ORDER BY age ASC,hiredate DESC;

-- 查询性别为男,年龄在20-22岁以内的前2个员工信息,按照年龄升序
SELECT * FROM emp WHERE sex='男' AND (age BETWEEN 20 AND 22) ORDER BY age ASC LIMIT 2;

六、数据库设计

何为范式
好的数据库设计对数据库的存储性能和后期开发,都会产生重要的影响。建立规范的数据库需要满足一些规则来优化数据库的设计和存储,这些规则称为范式。

数据库三大范式就是设计表结构所遵循的规范,目的就是为了减少冗余,建立结构合理的数据库,而提高数据存储和使用的性能。

三大范式之间是具有依赖关系的,比如第二范式需要在第一范式的基础上进行,第三范式需要在第二范式的基础上进行。

1. 三大范式

1NF:字段原子性(不可再分)
遵循原子性。即表中字段的数据,不可以在拆分。

2NF:消除部分依赖(有主键)
一个表只能描述一件事情。

3NF:消除传递依赖(外键关联)
在满足第二范式的情况下,消除传递依赖。

1.数据库约束

对表中的数据进行限制,保证数据的正确性、有效性和完整性。如果一个表添加了约束,不正确的数据将无法插入到表中。
有点像过滤器

2. 约束类型

对表中的数据进行限制,保证数据的正确性、有效性和完整性。如果一个表添加了约束,不正确的数据将无法插入到表中。

  • 非空约束: 限制该字段的数据不能为NULL NOT NULL
  • 唯一约束:限制该字段的数据都是唯一的,不重复 UNIQUE
  • 主键约束:主键是一行数据的唯一表示,非空且唯一 PRIMARY KEY
  • 检查约束(8.0之后的版本才有效):保证字段满足某一个条件 CHECK
  • 默认约束:保存数据时,如果不指定该字段的值,则使用默认值 DEFUALT
  • 外键约束:用于让两张表的数据之间建立连接,保证数据的一致性 FOREIGN KEY

3. 外键操作

创建外键约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- 创建外键约束
create table emp(
id int primary key auto_increment,
name varchar(20),
dno int, -- 外键(从表)
-- constraint[约束] fk_dno[约束名] foreign key [外键约束](外键字段名) references[引用]
constraint fk_dno foreign key(dno) references dept(dno) on update cascade on delete cascade
)

create table dept(
dno int primary key auto_increment,-- 主键(主键)
dname varchar(20)
)

insert into emp values(null,'jack',10);
insert into emp values(null,'Tom',10);
insert into emp values(null,'Lucy',20);

删除外键

1
2
3
4
5
-- 删除外键
alter table emp drop foreign key fk_dno;

-- 在emp表存在的情况下添加外键
alter table emp add constraint fk_dno foreign key(dno) references dept(dno);

级联操作

级联操作指的是在数据库表之间建立起一种关联关系,使得对一个表的操作(如更新或删除)能够自动触发对相关联表中数据的相应操作。

  • 级联更新(ON UPDATE CASCADE)
  • 级联删除(ON DELETE CASCADE)

当设置了级联更新,如果主表(包含外键的表)中的数据发生变化,那么从表(被引用的表)中相应的数据也会自动更新。同样,当设置了级联删除,如果主表中的数据被删除,从表中相应的数据也会被自动删除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 创建学生表
CREATE TABLE stu(
sid INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20) NOT NULL
) TYPE=InnoDB CHARSET=utf8;

-- 创建成绩表
CREATE TABLE sc(
scid INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
sid INT UNSIGNED NOT NULL,
score VARCHAR(20) DEFAULT '0',
INDEX (sid), -- 外键必须加索引
FOREIGN KEY (sid) REFERENCES stu(sid) ON DELETE CASCADE ON UPDATE CASCADE
) TYPE=InnoDB CHARSET=utf8;

通过FOREIGN KEY (sid) REFERENCES stu(sid)建立了外键关系,并通过ON DELETE CASCADE和ON UPDATE CASCADE指定了级联删除和级联更新的行为。

SQL 多表连接分类详解

1. 内连接 (INNER JOIN)

显式内连接

使用 INNER JOINON 关键字明确指定连接条件:

1
2
3
SELECT s.StuName, c.ClassName
FROM StudentInfo s
INNER JOIN ClassInfo c ON s.SClassID = c.ClassID
隐式内连接

使用 WHERE 子句指定连接条件(不推荐):

1
2
3
SELECT s.StuName, c.ClassName
FROM StudentInfo s, ClassInfo c
WHERE s.SClassID = c.ClassID

特点

  • 只返回两表中匹配的行
  • 显式写法更清晰,推荐使用

2. 外连接 (OUTER JOIN)

左外连接 (LEFT JOIN)

返回左表所有行,右表无匹配则显示NULL:

1
2
3
SELECT s.StuName, e.ExamName
FROM StudentInfo s
LEFT JOIN StudentExam e ON s.StuID = e.EStuID
右外连接 (RIGHT JOIN)

返回右表所有行,左表无匹配则显示NULL:

1
2
3
SELECT s.StuName, e.ExamName
FROM StudentInfo s
RIGHT JOIN StudentExam e ON s.StuID = e.EStuID

内外连接对比

特性 内连接 外连接
结果集 只返回匹配行 返回匹配行+未匹配行(补NULL)
性能 通常更快 稍慢
用途 需要精确匹配时 需要保留所有记录时
NULL处理 不包含NULL 包含NULL

特点

  • 左连接保留左表全部数据
  • 右连接保留右表全部数据
  • 实际开发中左连接使用更频繁

3. 自连接 (SELF JOIN)s

表与自身连接,常用于层级数据查询:

1
2
3
4
-- 查询员工及其经理信息
SELECT e1.EmpName AS 员工, e2.EmpName AS 经理
FROM Employee e1
LEFT JOIN Employee e2 ON e1.ManagerID = e2.EmpID

特点

  • 必须使用表别名区分
  • 常用于组织结构、分类层级等场景

4. 交叉连接 (CROSS JOIN)

返回两表的笛卡尔积(所有可能的组合):

1
2
3
SELECT s.StuName, c.ClassName
FROM StudentInfo s
CROSS JOIN ClassInfo c

特点

  • 结果行数 = 表1行数 × 表2行数
  • 谨慎使用,可能产生大量数据

5. 子查询 (SUBQUERY)

WHERE 子句中的子查询

1
2
3
4
-- 查询高于平均分的学员
SELECT StuName, Score
FROM StudentExam
WHERE Score > (SELECT AVG(Score) FROM StudentExam)

FROM 子句中的子查询(派生表)

1
2
3
4
5
6
7
8
9
-- 查询每个班级的平均分
SELECT c.ClassName, t.AvgScore
FROM ClassInfo c
JOIN (
SELECT SClassID, AVG(Score) AS AvgScore
FROM StudentInfo s
JOIN StudentExam e ON s.StuID = e.EStuID
GROUP BY SClassID
) t ON c.ClassID = t.SClassID

SELECT 子句中的子查询

1
2
3
4
5
6
7
8
9
10
-- 查询学生及其班级平均分
SELECT
s.StuName,
(SELECT AVG(Score)
FROM StudentExam e
WHERE e.EStuID IN (
SELECT StuID FROM StudentInfo
WHERE SClassID = s.SClassID
)) AS ClassAvgScore
FROM StudentInfo s

子查询类型

类型 说明 示例
标量子查询 返回单个值 SELECT ... WHERE col > (SELECT AVG(col) FROM ...)
列子查询 返回单列多行 SELECT ... WHERE col IN (SELECT col FROM ...)
行子查询 返回单行多列 SELECT ... WHERE (col1,col2) = (SELECT col1,col2 FROM ...)
表子查询 返回多行多列 FROM (SELECT ...) AS t

七、高级特性

1. 事务控制

1
2
3
4
5
6
7
START TRANSACTION;
UPDATE accounts SET balance = balance - 1000
WHERE user_id = 1;
UPDATE accounts SET balance = balance + 1000
WHERE user_id = 2;
COMMIT;
-- 或 ROLLBACK;

2. 视图创建

1
2
3
4
CREATE VIEW emp_dept_view AS
SELECT e.name, e.salary, d.department_name
FROM employees e JOIN departments d
ON e.department_id = d.id;

3. 索引优化

1
2
3
4
5
6
7
-- 创建索引
CREATE INDEX idx_product_name
ON products(name);

-- 联合索引
CREATE INDEX idx_emp_name_dept
ON employees(name, department_id);

八、经典面试题

1. CHAR vs VARCHAR

char和varchar都可以描述字符串,char是固定字符串,无论使用几个字符串都占满全部。varchar可变字符串,使用几个字符串就占用几个。

  • CHAR:固定长度,适合存储定长数据(如身份证号)
  • VARCHAR:可变长度,适合存储变长数据(如用户名)

例子:
CHAR(11) – 固定长度
一定要初始化一个长度,键入内容不到该长度会用空格补齐,不可超出定义长度,内存占用小
VARCHAR(50) – 可变长度
一定要初始化一个长度,用多少就分配多少长度,可以超出定义长度

2. DELETE vs TRUNCATE

  • DELETE:逐行删除,可回滚,不影响自增
  • TRUNCATE:整表删除,不可回滚,重置自增

1.删除的方式不同

  • ​ delete命令执行的时候会产生数据的日志文件,而日志记录需要消耗时间,方便数据回滚恢复。

  • ​ truncate命令执行的时候不回产生数据日志,因此比delete更快。

2.表结构的影响

  • ​ delete不会影响表的结构。

  • ​ truncate会把表的自增进行重置和索引恢复初始大小。

3. WHERE vs HAVING

  • WHERE:分组前过滤,不能使用聚合函数
  • HAVING:分组后过滤,可以使用聚合函数

执行时机不同: where是在分组之前进行过滤,不满足where条件,不参与分组,而having是分组之后对结果进行筛选。
判断条件不同:where 不能对聚合函数进行判断,having是可以的

九、实用技巧

1. 数据导出导入

1
2
3
4
5
# 导出整个数据库
mysqldump -uroot -p database > backup.sql

# 导入数据
mysql -uroot -p database < backup.sql

2. 性能分析

1
2
3
4
5
6
-- 查看执行计划
EXPLAIN SELECT * FROM products WHERE price > 100;

-- 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 2;

3. 安全设置

1
2
3
4
-- 创建用户并授权
CREATE USER 'app_user'@'%' IDENTIFIED BY 'password123';
GRANT SELECT, INSERT ON shop.* TO 'app_user'@'%';
FLUSH PRIVILEGES;

十、实战练习

1. 多表联合查询

1
2
3
4
5
6
7
SELECT o.order_id, c.customer_name, p.product_name
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
WHERE o.order_date > '2023-01-01'
ORDER BY o.order_date DESC;

2. 复杂统计报表

1
2
3
4
5
6
7
8
9
10
SELECT 
YEAR(order_date) AS '年份',
MONTH(order_date) AS '月份',
COUNT(*) AS '订单数',
SUM(amount) AS '总金额',
AVG(amount) AS '平均金额'
FROM orders
GROUP BY YEAR(order_date), MONTH(order_date)
HAVING COUNT(*) > 10
ORDER BY 年份 DESC, 月份 ASC;

3. 数据清洗转换

1
2
3
4
5
6
7
8
-- 将旧数据迁移到新表
INSERT INTO new_employees(id, full_name, contact)
SELECT
emp_id,
CONCAT(first_name, ' ', last_name),
COALESCE(mobile, email, '无联系方式')
FROM old_employee_data
WHERE status = 'active';