SQL
SQL
# SQL学习笔记目录
- 1 SQL基础
- 1.1 SQL通用语法
- 1.2 SQL分类
- 2 DDL
- 2.1 数据库操作
- 2.1.1 查询
- 2.1.2 创建
- 2.1.3 删除
- 2.1.4 使用
- 2.2 表操作
- 2.2.1 查询
- 2.2.2 创建
- 2.2.3 修改
- 2.2.4 删除
- 2.3 SQL常用字段和字段类型
- 2.3.1 一、数值类型(Numeric Types)
- 2.3.1.1 整数类型
- 2.3.1.2 小数类型
- 2.3.2 二、字符串类型(String Types)
- 2.3.2.1 定长 vs 变长
- 2.3.2.2 大文本类型
- 2.3.3 三、日期和时间类型(Date & Time)
- 2.3.4 四、枚举与集合类型(慎用)
- 2.3.5 五、二进制与特殊类型
- 2.3.6 六、选择字段类型的黄金原则
- 2.3.7 七、常见字段类型搭配示例
- 2.3.1 一、数值类型(Numeric Types)
- 2.1 数据库操作
- 3 DML
- 3.1 添加数据
- 3.2 修改数据
- 3.3 删除数据
- 4 DQL
- 4.1 语法
- 4.1.1 常见查询分类
- 4.2 基本查询
- 4.2.1 查询多个字段
- 4.2.2 设置别名
- 4.2.3 去除重复记录
- 4.3 条件查询
- 4.3.1 语法
- 4.3.1.1 比较运算符
- 4.3.1.2 逻辑运算符
- 4.3.1 语法
- 4.4 聚合函数
- 4.4.1 介绍
- 4.4.2 常见聚合函数
- 4.4.3 语法
- 4.5 分组查询
- 4.5.1 语法
- 4.5.2
WHERE与HAVING区别
- 4.6 排序查询
- 4.6.1 语法
- 4.6.2 排序方式
- 4.7 分页查询
- 4.7.1 语法
- 4.8 执行顺序
- 4.8.1 编写顺序
- 4.8.2 执行顺序
- 4.1 语法
- 5 DCL
- 5.1 管理用户
- 5.1.1 查询用户
- 5.1.2 创建用户
- 5.1.3 修改用户密码
- 5.1.4 删除用户
- 5.2 权限控制
- 5.2.1 常用权限
- 5.2.2 查询权限
- 5.2.3 授予权限
- 5.2.4 撤销权限
- 5.1 管理用户
- 6 常用函数
- 6.1 常用函数概述
- 6.2 字符串函数
- 6.2.1 函数说明
- 6.2.2 示例
- 6.2.3 案例
- 6.3 数值函数
- 6.3.1 函数说明
- 6.3.2 示例
- 6.3.3 案例
- 6.4 日期函数
- 6.4.1 函数说明
- 6.4.2 示例
- 6.4.3 案例
- 6.5 流程函数
- 6.5.1 函数说明
- 6.5.2 示例
- 6.5.3 案例1
- 6.5.4 案例2
- 6.6 窗口函数
- 6.6.1 概述
- 6.6.2 基本语法
- 6.6.3 常见窗口函数
- 6.6.4 排名函数示例
- 6.6.5 分析函数示例
- 7 约束
- 7.1 概述
- 7.2 常见约束
- 7.3 约束示例
- 7.4 外键约束
- 7.4.1 介绍
- 7.4.2 添加外键
- 7.4.3 删除外键
- 7.4.4 删除 / 更新行为
- 7.4.5 指定级联行为
- 8 多表查询
- 8.1 多表关系
- 8.1.1 一对多
- 8.1.2 多对多
- 8.1.3 一对一
- 8.2 多表查询概述
- 8.2.1 概述
- 8.2.2 分类
- 8.2.3 连接查询分类
- 8.3 内连接
- 8.3.1 隐式内连接
- 8.3.2 显式内连接
- 8.3.3 示例
- 8.4 外连接
- 8.4.1 左外连接
- 8.4.2 右外连接
- 8.4.3 示例
- 8.5 自连接
- 8.5.1 语法
- 8.5.2 示例
- 8.6 联合查询
- 8.6.1 语法
- 8.6.2 说明
- 8.6.3 示例
- 8.7 子查询
- 8.7.1 概述
- 8.7.1.1 按结果分类
- 8.7.1.2 按位置分类
- 8.7.1 概述
- 8.8 标量子查询
- 8.8.1 常用操作符
- 8.8.2 示例
- 8.9 列子查询
- 8.9.1 常用操作符
- 8.9.2 示例
- 8.10 行子查询
- 8.10.1 常用操作符
- 8.10.2 示例
- 8.11 表子查询
- 8.11.1 常用操作符
- 8.11.2 示例
- 8.12 多表查询案例
- 8.12.1 查询所有部门信息,并统计部门员工人数
- 8.12.2 查询所有学生的选课情况
- 8.1 多表关系
- 9 事务
- 9.1 事务简介
- 9.2 事务操作
- 9.2.1 方式一:控制自动提交
- 9.2.2 方式二:手动开启事务
- 9.2.3 转账案例
- 9.3 事务四大特性
- 9.4 并发事务问题
- 9.4.1 脏读
- 9.4.2 不可重复读
- 9.4.3 幻读
- 9.5 事务隔离级别
- 9.5.1 隔离级别说明
- 9.5.2 查看隔离级别
- 9.5.3 设置隔离级别
- 9.6 补充-事务控制过程
- 9.6.1 未控制事务
- 9.6.1.1 正常情况
- 9.6.1.2 异常情况
- 9.6.2 控制事务一
- 9.6.2.1 查看 / 设置事务提交方式
- 9.6.2.2 提交事务
- 9.6.2.3 回滚事务
- 9.6.3 控制事务二
- 9.6.3.1 开启事务
- 9.6.3.2 提交事务
- 9.6.3.3 回滚事务
- 9.6.1 未控制事务
- 10 综合案例-员工与部门管理
- 10.1 案例说明
- 10.2 创建数据库并切换
- 10.3 创建表
- 10.4 插入数据
- 10.5 修改数据
- 10.6 删除数据
- 10.7 基础查询
- 10.7.1 查询所有员工
- 10.7.2 查询指定字段并设置别名
- 10.7.3 去重查询
- 10.8 条件查询
- 10.8.1 比较 / 范围 / 模糊 / 空值判断
- 10.8.2 IN / OR / NOT
- 10.9 聚合、分组、排序、分页
- 10.9.1 聚合函数
- 10.9.2 分组查询
- 10.9.3 排序 + 分页
- 10.10 常用函数
- 10.10.1 字符串函数
- 10.10.2 日期函数
- 10.10.3 流程函数
- 10.11 多表查询
- 10.11.1 内连接
- 10.11.2 左外连接
- 10.11.3 自连接
- 10.11.4 联合查询
- 10.12 子查询
- 10.12.1 标量子查询
- 10.12.2 列子查询
- 10.12.3 行子查询
- 10.12.4 表子查询
- 10.13 事务
- 10.14 执行顺序理解
- 11 正则表达式
- 11.1 概述
- 11.2 常用元字符
- 11.3 常用正则函数
- 11.4 示例操作
- 11.4.1 基础匹配 (
REGEXP/RLIKE) - 11.4.2 提取子串 (
REGEXP_SUBSTR) - 11.4.3 替换子串 (
REGEXP_REPLACE) - 11.4.4 查找位置 (
REGEXP_INSTR)
- 11.4.1 基础匹配 (
- 11.5 注意事项
1 SQL基础
1.1 SQL通用语法
- SQL 语句可以单行或多行书写,以分号
;结尾。 - SQL 语句可以使用空格 / 缩进来增强语句的可读性。
- MySQL 数据库的 SQL 语句不区分大小写,关键字建议使用大写。
- 注释:
- 单行注释:
-- 注释内容或# 注释内容 - 多行注释:
/* 注释内容 */
- 单行注释:
1.2 SQL分类
| 分类 | 全称 | 说明 |
|---|---|---|
DDL |
Data Definition Language | 数据定义语言,用来定义数据库对象(数据库、表、字段) |
DML |
Data Manipulation Language | 数据操作语言,用来对数据库表中的数据进行增删改 |
DQL |
Data Query Language | 数据查询语言,用来查询数据库中表的记录 |
DCL |
Data Control Language | 数据控制语言,用来创建数据库用户、控制数据库的访问权限 |
2 DDL
2.1 数据库操作
2.1.1 查询
查询所有数据库
1
SHOW DATABASES;查询当前数据库
1
SELECT DATABASE();
2.1.2 创建
1 | |
2.1.3 删除
1 | |
2.1.4 使用
1 | |
2.2 表操作
2.2.1 查询
查询当前数据库所有表
1
SHOW TABLES;查询表结构
1
DESC 表名;查询指定表的建表语句
1
SHOW CREATE TABLE 表名;
2.2.2 创建
1 | |
注意:[...]) 为可选参数,最后一个字段后面没有逗号
2.2.3 修改
添加字段
1
ALTER TABLE 表名 ADD 字段名 类型 [COMMENT 注释];修改字段类型
1
ALTER TABLE 表名 MODIFY 字段名 新类型 [COMMENT 注释];修改字段名和类型
1
ALTER TABLE 表名 CHANGE 旧字段名 新字段名 新类型 [COMMENT 注释];删除字段
1
ALTER TABLE 表名 DROP 字段名;修改表名
1
ALTER TABLE 旧表名 RENAME TO 新表名;
2.2.4 删除
删除表
1
DROP TABLE [ IF EXISTS ] 表名;删除指定表,并重新创建该表
1
TRUNCATE TABLE 表名;
2.3 SQL常用字段和字段类型
在 SQL(特别是 MySQL)中,字段类型(也叫数据类型)决定了该字段能存储什么种类的数据、占用多少存储空间、以及支持哪些操作。选择合适的数据类型对数据库的性能、存储效率和数据准确性至关重要。
2.3.1 一、数值类型(Numeric Types)
2.3.1.1 整数类型
| 类型 | 范围 | 占用空间 | 适用场景 |
|---|---|---|---|
TINYINT |
-128 ~ 127(有符号)0 ~ 255(无符号) | 1 字节 | 状态标志(如 status: 0/1)、性别 |
SMALLINT |
-32,768 ~ 32,767 | 2 字节 | 小计数器、年份 |
MEDIUMINT |
-8,388,608 ~ 8,388,607 | 3 字节 | 中等ID或计数 |
INT / INTEGER |
-21亿 ~ 21亿 | 4 字节 | 用户ID、订单ID(中小系统) |
BIGINT |
±9.2×10¹⁸ | 8 字节 | 高并发ID、分布式系统主键 |
建议:
- 主键优先用
BIGINT(避免 ID 耗尽) - 状态字段用
TINYINT UNSIGNED(0~255)
2.3.1.2 小数类型
| 类型 | 说明 | 适用场景 |
|---|---|---|
DECIMAL(M,D) |
精确小数,M=总位数,D=小数位例如 DECIMAL(10,2) 表示最多 8 位整数 + 2 位小数 |
金额、价格、财务数据(必须用它!) |
FLOAT |
单精度浮点(约7位有效数字) | 科学计算、不需精确的场景 |
DOUBLE |
双精度浮点(约15位有效数字) | 地理坐标、物理量 |
重要:不要用 FLOAT/DOUBLE 存金额!会有精度丢失(如 0.1 + 0.2 ≠ 0.3)。
正确做法:price DECIMAL(10,2)
2.3.2 二、字符串类型(String Types)
2.3.2.1 定长 vs 变长
| 类型 | 最大长度 | 特点 | 适用场景 |
|---|---|---|---|
CHAR(n) |
0~255 字符 | 固定长度,不足补空格 | 国家代码(’CN’)、MD5(32位) |
VARCHAR(n) |
0~65,535 字节 | 可变长度,节省空间 | 姓名、标题、邮箱、地址 |
建议:
- 大多数文本用
VARCHAR - 长度明确且固定用
CHAR
2.3.2.2 大文本类型
| 类型 | 最大容量 | 适用场景 |
|---|---|---|
TINYTEXT |
255 字节 | 极短描述 |
TEXT |
65,535 字节(≈64KB) | 文章内容、评论 |
MEDIUMTEXT |
16MB | 长文章、日志 |
LONGTEXT |
4GB | 小说、PDF文本提取 |
提示:TEXT 类型不能有默认值,且可能影响查询性能(尽量不 SELECT *)
2.3.3 三、日期和时间类型(Date & Time)
| 类型 | 格式 | 范围 | 适用场景 |
|---|---|---|---|
DATE |
YYYY-MM-DD |
1000-01-01 ~ 9999-12-31 | 生日、日期选择 |
TIME |
HH:MM:SS |
-838:59:59 ~ 838:59:59 | 持续时间、时间段 |
DATETIME |
YYYY-MM-DD HH:MM:SS |
1000-01-01 00:00:00 ~ 9999-12-31 23:59:59 | 创建时间、更新时间(推荐) |
TIMESTAMP |
同上 | 1970-01-01 00:00:01 UTC ~ 2038-01-19 03:14:07 UTC | 需要自动时区转换的场景(但有 2038 年问题) |
建议:
- 优先使用
DATETIME(范围大、无时区副作用) - 自动设置当前时间:
1
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
2.3.4 四、枚举与集合类型(慎用)
| 类型 | 说明 | 示例 |
|---|---|---|
ENUM('值1','值2',...) |
枚举,只能选预定义值 | gender ENUM('男','女','未知') |
SET('值1','值2',...) |
集合,可多选 | hobby SET('读书','电影','旅行') |
缺点:
- 修改选项需改表结构
- 不利于扩展(微服务、多语言场景)
替代方案:用TINYINT+ 代码注释,或单独建字典表
2.3.5 五、二进制与特殊类型
| 类型 | 用途 |
|---|---|
BLOB / LONGBLOB |
存储图片、文件等二进制数据(一般不推荐,建议存文件路径) |
JSON(MySQL 5.7+) |
存储 JSON 文档,支持 JSON 查询 |
BOOLEAN |
实际是 TINYINT(1) 的别名 |
JSON 示例:
1 | |
查询:
1 | |
2.3.6 六、选择字段类型的黄金原则
- 够用就好:能用
TINYINT就不用INT,节省空间 = 提升性能。 - 避免 NULL:除非必要,字段设为
NOT NULL+ 默认值,减少判断复杂度。 - 金额用
DECIMAL:永远不要用浮点数存钱! - 时间用
DATETIME:避开TIMESTAMP的 2038 问题。 - 大文本分离:如果某字段很少查询,可拆到单独表(垂直分表)。
- 字符集统一:建议
utf8mb4(支持 emoji)。
2.3.7 七、常见字段类型搭配示例
| 业务含义 | 推荐类型 |
|---|---|
| 用户ID | BIGINT |
| 用户名 | VARCHAR(50) |
| 邮箱 | VARCHAR(150) |
| 手机号 | VARCHAR(20)(含国际区号) |
| 密码哈希 | VARCHAR(255)(兼容 bcrypt) |
| 性别 | TINYINT(0/1/2)或 CHAR(1)(’M’/‘F’) |
| 价格 | DECIMAL(10,2) |
| 商品描述 | TEXT |
| 注册时间 | DATETIME |
| 是否启用 | TINYINT(0/1) |
| 头像URL | VARCHAR(255) |
3 DML
3.1 添加数据
语法
1
2INSERT INTO 表名(字段名1, 字段名2, ...)
VALUES (值1, 值2, ...);给全部字段添加数据
1
2INSERT INTO 表名
VALUES (值1, 值2, ...);批量添加数据
1
2INSERT INTO 表名(字段名1, 字段名2, ...)
VALUES (值1, 值2, ...), (值1, 值2, ...), (值1, 值2, ...);1
2INSERT INTO 表名
VALUES (值1, 值2, ...), (值1, 值2, ...), (值1, 值2, ...);
注意:
- 插入数据时,指定的字段顺序需要与值的顺序一一对应。
- 字符串和日期类型数据应该包含在引号中。
- 插入的数据大小,应该在字段的规定范围内。
3.2 修改数据
1 | |
注意:
- 修改语句的条件可以有,也可以没有,如果没有条件,则会修改整张表的所有数据。
3.3 删除数据
1 | |
注意:
- DELETE 语句的条件可以有,也可以没有,如果没有条件,则会删除整张表的所有数据。
- DELETE 语句不能删除某一个字段的值(可以使用
UPDATE)。
4 DQL
4.1 语法
1 | |
4.1.1 常见查询分类
- 基本查询
- 条件查询(
WHERE) - 聚合函数(
COUNT、MAX、MIN、AVG、SUM) - 分组查询(
GROUP BY) - 排序查询(
ORDER BY) - 分页查询(
LIMIT)
4.2 基本查询
4.2.1 查询多个字段
1 | |
1 | |
4.2.2 设置别名
1 | |
4.2.3 去除重复记录
1 | |
4.3 条件查询
4.3.1 语法
1 | |
4.3.1.1 比较运算符
| 运算符 | 功能 |
|---|---|
> |
大于 |
>= |
大于等于 |
< |
小于 |
<= |
小于等于 |
= |
等于 |
<> / != |
不等于 |
BETWEEN ... AND ... |
在某个范围之内(包含最小值、最大值) |
IN(...) |
在 IN 之后的列表中的值,多选一 |
LIKE |
模糊匹配(_ 匹配单个字符,% 匹配任意个字符) |
IS NULL |
是 NULL |
4.3.1.2 逻辑运算符
| 运算符 | 功能 |
|---|---|
AND / && |
并且(多个条件同时成立) |
OR / ` |
|
NOT / ! |
非,不是 |
4.4 聚合函数
4.4.1 介绍
将一列数据作为一个整体,进行纵向计算。
4.4.2 常见聚合函数
| 函数 | 功能 |
|---|---|
COUNT |
统计数量 |
MAX |
最大值 |
MIN |
最小值 |
AVG |
平均值 |
SUM |
求和 |
- 注:COUNT函数还有一种用法
COUNT(DISTINCT 字段1, 字段2)意为(字段1, 字段2) 这个组合有多少种不同的值 - COUNT用法详解:
COUNT(*)= 数行数COUNT(字段)= 数该字段有值的行数COUNT(DISTINCT 字段)= 数该字段有几种不同的值
4.4.3 语法
1 | |
4.5 分组查询
4.5.1 语法
1 | |
4.5.2 WHERE 与 HAVING 区别
- 执行时机不同:
WHERE是分组之前进行过滤,不满足WHERE条件,不参与分组;HAVING是分组之后对结果进行过滤。 - 判断条件不同:
WHERE不能对聚合函数进行判断,而HAVING可以。
注意:
- 执行顺序:
WHERE> 聚合函数 >HAVING。 - 分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段无实际意义。
4.6 排序查询
4.6.1 语法
1 | |
4.6.2 排序方式
ASC:升序(默认值)DESC:降序
注意:
- 如果是多字段排序,当第一个字段值相同时,才会根据第二个字段进行排序。
4.7 分页查询
4.7.1 语法
1 | |
注意:
- 起始索引从 0 开始,起始索引 = (查询页码 - 1) * 每页显示记录数。
- 分页查询是数据库的方言,不同的数据库有不同的实现,MySQL 中是
LIMIT。 - 如果查询的是第一页数据,起始索引可以省略,直接简写为
LIMIT 10。
4.8 执行顺序
4.8.1 编写顺序
1 | |
4.8.2 执行顺序
1 | |
注意:
- 实际执行时并不是从
SELECT开始,而是先FROM再逐步过滤、分组、筛选、排序和分页。
5 DCL
5.1 管理用户
5.1.1 查询用户
1 | |
说明:
Host代表当前用户允许访问的主机。User代表访问该数据库的用户名。- 在 MySQL 中,需要通过
Host和User来唯一标识一个用户。
5.1.2 创建用户
1 | |
5.1.3 修改用户密码
1 | |
注意:
- 主机名可以使用
%通配。 - 这类 SQL 开发人员操作得比较少,主要是 DBA(数据库管理员)使用。
5.1.4 删除用户
1 | |
5.2 权限控制
5.2.1 常用权限
| 权限 | 说明 |
|---|---|
ALL / ALL PRIVILEGES |
所有权限 |
SELECT |
查询数据 |
INSERT |
插入数据 |
UPDATE |
修改数据 |
DELETE |
删除数据 |
ALTER |
修改表 |
DROP |
删除数据库 / 表 / 视图 |
CREATE |
创建数据库 / 表 |
5.2.2 查询权限
1 | |
5.2.3 授予权限
1 | |
5.2.4 撤销权限
1 | |
注意:
- 多个权限之间,使用逗号分隔。
- 授权时,数据库名和表名可以使用
*进行通配,代表所有。
6 常用函数
6.1 常用函数概述
MySQL 中常用函数主要分为五类:
- 字符串函数
- 数值函数
- 日期函数
- 流程函数
- 窗口函数
6.2 字符串函数
6.2.1 函数说明
| 函数 | 功能 |
|---|---|
CONCAT(S1,S2,...Sn) |
字符串拼接,将多个字符串拼接成一个字符串 |
LOWER(str) |
将字符串全部转为小写 |
UPPER(str) |
将字符串全部转为大写 |
LPAD(str,n,pad) |
左填充,用 pad 对左边进行填充,达到长度 n |
RPAD(str,n,pad) |
右填充,用 pad 对右边进行填充,达到长度 n |
TRIM(str) |
去掉字符串头部和尾部的空格 |
SUBSTRING(str,start,len) |
从指定位置截取指定长度的字符串 |
SUBSTRING_INDEX(str, delim, count) |
6.2.2 示例
1 | |
6.2.3 案例
将员工工号统一补齐为 5 位,不足部分左侧补 0:
1 | |
6.3 数值函数
6.3.1 函数说明
| 函数 | 功能 |
|---|---|
CEIL(x) |
向上取整 |
FLOOR(x) |
向下取整 |
MOD(x,y) |
返回 x / y 的模 |
RAND() |
返回 0~1 内的随机数 |
ROUND(x,y) |
对 x 四舍五入,保留 y 位小数 |
ABS() |
取绝对值 |
6.3.2 示例
1 | |
6.3.3 案例
生成 6 位随机验证码:
1 | |
6.4 日期函数
6.4.1 函数说明
| 函数 | 功能 |
|---|---|
CURDATE() |
返回当前日期 |
CURTIME() |
返回当前时间 |
NOW() |
返回当前日期和时间 |
YEAR(date) |
获取指定日期的年份 |
MONTH(date) |
获取指定日期的月份 |
DAY(date) |
获取指定日期的日 |
DATE_ADD(date, INTERVAL expr type) |
返回加上时间间隔后的日期 / 时间值 |
DATEDIFF(date1,date2) |
返回两个日期之间相差的天数(前减后) |
6.4.2 示例
1 | |
6.4.3 案例
查询所有员工的入职天数,并按入职天数倒序排序:
1 | |
6.5 流程函数
6.5.1 函数说明
| 函数 | 功能 |
|---|---|
IF(value, t, f) |
如果 value 为真,返回 t,否则返回 f |
IFNULL(value1, value2) |
如果 value1 不为空,返回 value1,否则返回 value2 |
CASE WHEN [条件] THEN [结果] ... ELSE [默认值] END |
类似多分支判断 |
CASE expr WHEN val1 THEN res1 ... ELSE default END |
判断表达式的值并返回对应结果 |
6.5.2 示例
1 | |
6.5.3 案例1
查询员工姓名和工作地址等级:
1 | |
6.5.4 案例2
按成绩判断等级:
1 | |
6.6 窗口函数
6.6.1 概述
- 窗口函数用于在保留明细行的前提下,对某一组数据进行统计、排序、排名或前后行分析。
- 它和普通聚合函数的区别是:聚合函数通常会“多行变一行”,而窗口函数不会减少结果行数。
- 窗口函数通常搭配
OVER()使用,可以在窗口中指定分组、排序和计算范围。
6.6.2 基本语法
1 | |
说明:
PARTITION BY:把结果集划分为多个窗口,类似分组,但不会合并行。ORDER BY:定义窗口内部的排序规则,常用于排名、累计计算等场景。- 如果只写
OVER(),表示在整个结果集范围内计算。 OVER关键字的作用是:新增一列,其计算规则写在括号里- 窗口函数不改变原表行数,只是在每一行旁边加一个或几个你想要的字段
OVER前面的函数返回的是按照OVER里定义的规则,为当前行计算出的单个值,也就是一个字段的值
6.6.3 常见窗口函数
| 函数 | 功能 |
|---|---|
ROW_NUMBER() OVER(...) |
为窗口中的每一行生成唯一顺序号,不会并列 |
RANK() OVER(...) |
排名,遇到并列时跳号(返回所有并列的) |
DENSE_RANK() OVER(...) |
密集排名,遇到并列时不跳号 |
SUM(col) OVER(...) |
在窗口内做累计求和 / 分组求和 |
AVG(col) OVER(...) |
在窗口内做平均值统计 |
COUNT(col) OVER(...) |
在窗口内统计记录数 |
MAX(col) OVER(...) |
在窗口内求最大值 |
MIN(col) OVER(...) |
在窗口内求最小值 |
LAG(col, n, default) OVER(...) |
取当前行前第 n 行的值 |
LEAD(col, n, default) OVER(...) |
取当前行后第 n 行的值 |
6.6.4 排名函数示例
按部门内员工工资从高到低排名:
1 | |
区别:
ROW_NUMBER():即使工资相同,也会给不同序号,如1、2、3RANK():工资相同会并列,后面的名次跳号,如1、1、3DENSE_RANK():工资相同会并列,但后面的名次不跳号,如1、1、2
6.6.5 分析函数示例
- 统计每个部门的员工人数,同时保留员工明细:
1 | |
- 统计每个部门的工资累计值:
1 | |
- 查看当前员工与上一位员工的工资差异:
1 | |
适用场景:
- TopN 排名
- 分组内排序
- 累计求和 / 累计平均
- 同比、环比、前后行对比
- 保留明细的同时做统计分析
7 约束
7.1 概述
- 约束是作用于表中字段上的规则,用于限制存储在表中的数据。
- 目的是保证数据库中数据的正确、有效性和完整性。
7.2 常见约束
| 约束 | 描述 | 关键字 |
|---|---|---|
| 非空约束 | 限制该字段不能为 NULL |
NOT NULL |
| 唯一约束 | 保证该字段所有数据唯一、不重复 | UNIQUE |
| 主键约束 | 一行数据的唯一标识,要求非空且唯一 | PRIMARY KEY |
| 默认约束 | 未指定该字段值时采用默认值 | DEFAULT |
| 检查约束 | 保证字段值满足某个条件 | CHECK |
| 外键约束 | 让两张表建立连接,保证一致性和完整性 | FOREIGN KEY |
注意:
- 约束可以在创建表时添加,也可以在修改表时添加。
7.3 约束示例
1 | |
7.4 外键约束
7.4.1 介绍
- 外键用来让两张表的数据之间建立连接,从而保证数据的一致性和完整性。
- 一般子表中保存外键字段,关联父表主键。
7.4.2 添加外键
1 | |
1 | |
1 | |
7.4.3 删除外键
1 | |
1 | |
7.4.4 删除 / 更新行为
| 行为 | 说明 |
|---|---|
NO ACTION |
有关联外键时,不允许删除 / 更新父表记录,默认行为 |
RESTRICT |
与 NO ACTION 一致 |
CASCADE |
父表删除 / 更新时,子表对应记录也同步删除 / 更新 |
SET NULL |
父表删除 / 更新时,子表外键字段置为 NULL |
SET DEFAULT |
父表变更时,子表外键列设为默认值,InnoDB 不支持 |
7.4.5 指定级联行为
1 | |
8 多表查询
8.1 多表关系
8.1.1 一对多
- 例子:部门 和 员工
- 关系:一个部门对应多个员工,一个员工对应一个部门
- 实现:在多的一方建立外键,指向一的一方主键
8.1.2 多对多
- 例子:学生 和 课程
- 关系:一个学生可以选多门课程,一门课程也可以被多个学生选择
- 实现:建立第三张中间表,中间表至少包含两个外键,分别关联两边主键
8.1.3 一对一
- 例子:用户 和 用户详情
- 关系:常用于单表拆分,把基础字段和详情字段拆到两张表
- 实现:在任意一方加入外键关联另一方主键,并且设置该外键唯一
UNIQUE
8.2 多表查询概述
8.2.1 概述
- 多表查询就是从多张表中查询数据。
- 直接查询多张表会出现笛卡尔积,需要通过连接条件消除无效数据。
8.2.2 分类
- 连接查询
- 子查询
8.2.3 连接查询分类
- 内连接:查询两张表交集部分数据
- 左外连接:查询左表所有数据,以及交集部分数据
- 右外连接:查询右表所有数据,以及交集部分数据
- 自连接:当前表与自身进行连接查询
8.3 内连接
8.3.1 隐式内连接
1 | |
8.3.2 显式内连接
1 | |
8.3.3 示例
1 | |
1 | |
注意:
- 一旦为表起了别名,就不能再使用原表名引用字段,只能使用别名。
8.4 外连接
8.4.1 左外连接
1 | |
8.4.2 右外连接
1 | |
8.4.3 示例
1 | |
1 | |
注意:
- 左外连接和右外连接可以相互替换,开发中更常用左外连接。
- 判断表的相对位置取决于表名在JOIN关键字的哪一边。
- JOIN 左边的就是左表,右边的就是右表,与 LEFT 或 RIGHT 关键字无关——这两个关键字只是告诉你保留哪一侧的全部数据。
- 我自己的理解:
ON:定义连接规则,根据规则把多张表横向拼接成一张包含所有列的“虚拟中间表”。
WHERE:对这个“虚拟中间表”进行纵向筛选(按行过滤),得到最终要用的数据。
8.5 自连接
8.5.1 语法
1 | |
8.5.2 示例
查询员工及其直属领导名字:
1 | |
注意:
- 自连接必须起别名,否则无法区分字段来自哪一张表。
8.6 联合查询
8.6.1 语法
1 | |
8.6.2 说明
UNION ALL:直接合并结果,不去重UNION:合并结果并去重
8.6.3 示例
1 | |
注意:
- 多个查询结果的列数必须一致,字段类型也应保持一致。
8.7 子查询
8.7.1 概述
- SQL 语句中嵌套
SELECT语句,称为子查询。 - 子查询外部语句可以是
INSERT、UPDATE、DELETE、SELECT。
8.7.1.1 按结果分类
- 标量子查询:结果为单个值
- 列子查询:结果为一列
- 行子查询:结果为一行
- 表子查询:结果为多行多列
8.7.1.2 按位置分类
WHERE之后FROM之后SELECT之后
8.8 标量子查询
8.8.1 常用操作符
=<>>>=<<=
8.8.2 示例
查询销售部所有员工:
1 | |
查询在“方东白”入职之后的员工信息:
1 | |
8.9 列子查询
8.9.1 常用操作符
INNOT INANYSOMEALL
8.9.2 示例
查询销售部和市场部所有员工:
1 | |
查询比财务部所有人工资都高的员工:
1 | |
查询比研发部任意一人工资高的员工:
1 | |
8.10 行子查询
8.10.1 常用操作符
=<>INNOT IN
8.10.2 示例
查询与“张无忌”的薪资和直属领导相同的员工:
1 | |
8.11 表子查询
8.11.1 常用操作符
IN
8.11.2 示例
查询与“鹿杖客”、“宋远桥”的职位和薪资相同的员工:
1 | |
查询入职日期在 2006-01-01 之后的员工信息及其部门信息:
1 | |
8.12 多表查询案例
8.12.1 查询所有部门信息,并统计部门员工人数
1 | |
8.12.2 查询所有学生的选课情况
1 | |
9 事务
9.1 事务简介
- 事务是一组操作的集合,是一个不可分割的工作单位。
- 事务中的操作要么同时成功,要么同时失败。
- MySQL 默认自动提交事务,执行一条
DML语句后会立即隐式提交。 - 典型场景:转账操作通常至少包含“扣款”和“加款”两个步骤,这两个步骤必须作为一个整体执行。
- 如果执行过程中中途报错,而前面的语句已经生效、后面的语句没有生效,就会造成数据不一致。
- 因此事务的核心价值就是:在业务操作成功时统一提交,在业务操作失败时统一回滚,把数据恢复到事务开始之前的状态。
9.2 事务操作
9.2.1 方式一:控制自动提交
1 | |
说明:
- 把
autocommit设为0后,后续执行的DML语句不会自动提交,需要手动执行COMMIT。 - 如果执行过程中出现异常,可以执行
ROLLBACK撤销本次事务中的修改。
9.2.2 方式二:手动开启事务
1 | |
说明:
- 这种方式更常用,也更清晰:显式开启事务,业务执行完成后再决定提交或回滚。
START TRANSACTION和BEGIN都可以用来开启事务。
9.2.3 转账案例
1 | |
9.3 事务四大特性
| 特性 | 说明 |
|---|---|
| 原子性(Atomicity) | 事务是最小操作单元,要么全部成功,要么全部失败 |
| 一致性(Consistency) | 事务完成时,所有数据都必须保持一致状态 |
| 隔离性(Isolation) | 多个事务之间相互隔离,互不干扰 |
| 持久性(Durability) | 事务一旦提交或回滚,对数据的改变就是永久的 |
9.4 并发事务问题
9.4.1 脏读
- 一个事务读到另一个事务还没有提交的数据。
- 比如事务 A 修改了一条记录但还没提交,事务 B 已经读取到了这条新值;如果事务 A 随后回滚,那么事务 B 读到的就是“脏数据”。
9.4.2 不可重复读
- 一个事务先后读取同一条记录,两次读到的数据不同。
- 一般是因为两个读取之间,另一个事务已经提交了对该记录的更新。
9.4.3 幻读
- 一个事务按条件查询时没有对应数据,但插入时却发现这行数据已存在,像出现了“幻影”。
- 本质上是同一事务中按照相同条件前后读取,结果集的“行数”发生了变化,通常和其他事务的插入操作有关。
9.5 事务隔离级别
9.5.1 隔离级别说明
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
READ UNCOMMITTED |
会 | 会 | 会 |
READ COMMITTED |
不会 | 会 | 会 |
REPEATABLE READ(默认) |
不会 | 不会 | 会 |
SERIALIZABLE |
不会 | 不会 | 不会 |
9.5.2 查看隔离级别
1 | |
9.5.3 设置隔离级别
1 | |
注意:
- 隔离级别越高,数据越安全,但性能越低。
REPEATABLE READ是 MySQL 中常见的默认隔离级别。- 记忆规律可以简单理解为:隔离级别越往上,能解决的并发问题越多。
9.6 补充-事务控制过程
9.6.1 未控制事务
9.6.1.1 正常情况
1 | |
9.6.1.2 异常情况
1 | |
9.6.2 控制事务一
9.6.2.1 查看 / 设置事务提交方式
1 | |
9.6.2.2 提交事务
1 | |
9.6.2.3 回滚事务
1 | |
9.6.3 控制事务二
9.6.3.1 开启事务
1 | |
9.6.3.2 提交事务
1 | |
9.6.3.3 回滚事务
1 | |
10 综合案例-员工与部门管理
10.1 案例说明
下面这个案例尽量串起常用 / 核心语法:数据库、建表、约束、外键、增删改查、函数、多表查询、子查询、事务。
10.2 创建数据库并切换
1 | |
10.3 创建表
1 | |
1 | |
10.4 插入数据
1 | |
1 | |
10.5 修改数据
统一把工号补齐为 5 位:
1 | |
给研发部员工涨薪 1000:
1 | |
10.6 删除数据
删除年龄小于 25 的员工:
1 | |
10.7 基础查询
10.7.1 查询所有员工
1 | |
10.7.2 查询指定字段并设置别名
1 | |
10.7.3 去重查询
1 | |
10.8 条件查询
10.8.1 比较 / 范围 / 模糊 / 空值判断
1 | |
10.8.2 IN / OR / NOT
1 | |
10.9 聚合、分组、排序、分页
10.9.1 聚合函数
1 | |
10.9.2 分组查询
1 | |
10.9.3 排序 + 分页
1 | |
10.10 常用函数
10.10.1 字符串函数
1 | |
10.10.2 日期函数
1 | |
10.10.3 流程函数
1 | |
10.11 多表查询
10.11.1 内连接
1 | |
10.11.2 左外连接
1 | |
10.11.3 自连接
1 | |
10.11.4 联合查询
1 | |
10.12 子查询
10.12.1 标量子查询
查询研发部员工:
1 | |
10.12.2 列子查询
查询市场部和销售部员工:
1 | |
10.12.3 行子查询
查询与“李四”薪资和直属领导都相同的员工:
1 | |
10.12.4 表子查询
查询 2020 年以后入职员工及其部门信息:
1 | |
10.13 事务
模拟转账:
1 | |
10.14 执行顺序理解
上面这类复杂查询的执行顺序通常是:
1 | |
11 正则表达式
11.1 概述
正则表达式(Regular Expression)是一种强大的文本模式匹配工具,在 SQL 中常用于复杂字符串的查找、替换和提取操作。MySQL 提供了对正则表达式的内置支持。
11.2 常用元字符
| 元字符 | 描述 | 示例 |
|---|---|---|
^ |
匹配字符串的开始 | ^a 匹配以 a 开头的字符串 |
$ |
匹配字符串的结束 | a$ 匹配以 a 结尾的字符串 |
. |
匹配任意单个字符(除换行符外) | a.c 匹配 abc、a1c 等 |
* |
匹配前一个字符零次或多次 | a*b 匹配 b、ab、aab 等 |
+ |
匹配前一个字符一次或多次 | a+b 匹配 ab、aab 等,不匹配 b |
? |
匹配前一个字符零次或一次 | a?b 匹配 b、ab |
[] |
匹配括号内的任意一个字符 | [abc] 匹配 a、b 或 c |
[^] |
匹配不在括号内的任意字符 | [^abc] 匹配除 a、b、c 外的任意字符 |
| |
匹配两个或多个分支选择(或) | a|b 匹配 a 或 b |
{n} |
匹配前一个字符恰好 n 次 | a{3} 匹配 aaa |
{n,} |
匹配前一个字符至少 n 次 | a{2,} 匹配 aa、aaa 等 |
{n,m} |
匹配前一个字符至少 n 次,最多 m 次 | a{1,3} 匹配 a、aa、aaa |
11.3 常用正则函数
MySQL 8.0 引入了多个强大的正则表达式函数(旧版本主要使用 REGEXP / RLIKE 运算符)。
| 函数 / 运算符 | 功能说明 |
|---|---|
expr REGEXP pat / expr RLIKE pat |
模式匹配,如果 expr 匹配 pat 则返回 1,否则返回 0 |
REGEXP_LIKE(expr, pat[, match_type]) |
同 REGEXP,但可以指定匹配参数(如区分大小写) |
REGEXP_INSTR(expr, pat[, pos[, occurrence[, return_option[, match_type]]]]) |
返回匹配模式的子串的起始索引(找不到返回 0) |
REGEXP_REPLACE(expr, pat, repl[, pos[, occurrence[, match_type]]]) |
替换匹配模式的子串 |
REGEXP_SUBSTR(expr, pat[, pos[, occurrence[, match_type]]]) |
返回匹配模式的子串 |
11.4 示例操作
11.4.1 基础匹配 (REGEXP / RLIKE)
查询姓名以“张”开头或以“三”结尾的员工:
1 | |
查询工号包含连续两个数字的员工:
1 | |
11.4.2 提取子串 (REGEXP_SUBSTR)
从邮箱中提取域名部分:
1 | |
11.4.3 替换子串 (REGEXP_REPLACE)
将手机号中间四位替换为星号:
1 | |
11.4.4 查找位置 (REGEXP_INSTR)
查找第一个非数字字符出现的位置:
1 | |
11.5 注意事项
- MySQL 正则表达式默认不区分大小写,若要区分大小写,可以使用
BINARY关键字(如REGEXP BINARY 'a')或在match_type参数中指定'c'。 - 正则表达式查询通常会导致全表扫描,无法使用索引,在数据量大的表上使用需谨慎。
- 在匹配具有特殊含义的元字符(如
.、*、[等)本身时,需要使用转义符(MySQL 中通常使用\\,如\\.)。
评论区