目录
1. 数据库约束
1.1约束类型
2. 表的设计
2.1 一对一
2.2 一对多
2.3 多对多
3. 新增
4. 查询
4.1 聚合查询
4.2 GROUP BY
4.3 HAVING
4.4 联合查询
4.5 内连接
4.5.1 内连接的核心概念
4.5.2 内连接的语法
4.5.3 ON 与 WHERE 的区别
4.6 自连接
4.6.1 定义
4.6.2 核心用途
4.7 外连接
4.7.1 定义
4.7.2 核心用途
4.8 子查询
4.8.1 子查询概念
4.9 合并查询
5. 索引
5.1 概念
5.2 索引的作用
5.3 索引的使用
5.4 B+树
5.4.1 B+ 树的核心特性
5.4.2 B+树的优势
6 事务
6.1 概念
6.2 事务的 ACID 特性
6.3 事务的隔离级别
6.3.1 READ UNCOMMITTED (读未提交)
6.3.2 READ COMMITTED(读已提交)
6.3.3 REPEATABLE READ(可重复读,MySQL 默认级别)
6.3.4 SERIALIZABLE(串行化)
1. 数据库约束
1.1约束类型
• NOT NULL - 某列不能储存 NULL 值
DROP TABLE IF EXISTS student;
CREATE TABLE student (
id INT NOT NULL,
sn INT,
name VARCHAR(20),
qq_mail VARCHAR(20)
);
• UNIQUE - 保证某列的每行必须有唯一的值
DROP TABLE IF EXISTS student;
CREATE TABLE student (
id INT ,
sn INT UNIQUE,
name VARCHAR(20),
qq_mail VARCHAR(20)
);
• DEFAULT - 规定没有给列赋值时的默认值
DROP TABLE IF EXISTS student;
CREATE TABLE student (
id INT NOT NULL,
sn INT UNIQUE,
name VARCHAR(20) DEFAULT 'unkown',
qq_mail VARCHAR(20)
);
• PRIMARY KEY - NOT 和 UNIQUE 的结合。确保某列有唯一标识,有助于更容易的快速找到表中的一个特定的记录
DROP TABLE IF EXISTS student;
CREATE TABLE student (
id INT PRIMARY KEY auto_increment
sn INT UNIQUE,
name VARCHAR(20) DEFAULT 'unkown',
qq_mail VARCHAR(20)
);
• FOREIGN KEY - 保证一个表中的数据匹配另一个表中的值的参照完整性
外键用于关联其他表的主键或者唯一键,语法:
foreign key (字段名) references 主表(列)
DROP TABLE IF EXISTS classes;
CREATE TABLE classes (
id INT PRIMARY KEY auto_increment,
name VARCHAR(20),
`desc` VARCHAR(100)
);
DROP TABLE IF EXISTS student;
CREATE TABLE student (
id INT PRIMARY KEY auto_increment,
sn INT UNIQUE,
name VARCHAR(20) DEFAULT 'unkown',
qq_mail VARCHAR(20),
classes_id int,
FOREIGN KEY (classes_id) REFERENCES classes(id)
);
• CHECK - CHECK 约束是 SQL 中用于限制列中可以存储的值的约束条件。它允许你定义一个布尔表达式,该表达式必须为真,否则数据将不被允许插入或更新到表中。
2. 表的设计
2.1 一对一
一个学生只能有一个账号,一个账号只能被一个学生持有
设计表做法:
两个表,使用 id 来进行联系
student (studentId, name, accountId)
account (accountId, accountName, password)
2.2 一对多
一个学生只能在一个班级,一个班级可以包含多个学生
设计表做法:
2.3 多对多
一个课程可有多个学生,一个学生可以选择多门课程
设计表做法:
此时我们就需要引入关联表,我们可以设计三张表
(1) student (studentId, studentName ......)
(2) course (courseId, courseName ......)
此时不管我们是在 student 表里添加 courseId 还是在 course 表里 添加 studentId 都只能表示一对多的关系。所以我们可以创建一个关联表来吧 student 表和 course 表都连接起来。
(3) student_course (studentId, courseId )
3. 新增
我们可以通过 SELECT 代替 VALUES 来提供数据
INSERT INTO 目标表 (列1, 列2)
SELECT 源列1, 源列2 FROM 源表;
-- 创建用户表
DROP TABLE IF EXISTS test_user;
CREATE TABLE test_user (
id INT primary key auto_increment,
name VARCHAR(20) comment '姓名',
age INT comment '年龄',
email VARCHAR(20) comment '邮箱',
sex varchar(1) comment '性别',
mobile varchar(20) comment '手机号'
);
-- 将学生表中的所有数据复制到用户表
insert into test_user(name, email) select name, qq_mail from student;
需要注意的是,这是属于 INSERT 语法范畴,数据来源于动态 SELECT 子查询。先运行子查询,之后会把子查询的结果按照对应顺序插入到 INSERT 表中。
4. 查询
4.1 聚合查询
聚合查询函数如下:
• COUNT
COUNT(*) 会计入包含 NULL 值的行,它返回表中所有记录的条数,无论是否某个列存在 NULL。而 COUNT(具体列名) 只会统计该列中 非 NULL 值的数量。
-- 统计班级共有多少同学
SELECT COUNT(*) FROM student;
• SUM
-- 统计数学成绩总分
SELECT SUM(math) FROM exam_result
• AVG
-- 统计平均总分
SELECT AVG(chinese + math + english) 平均总分 FROM exam_result;
• MAX
-- 返回英语最高分
SELECT MAX(english) FROM exam_result;
• MIN
-- 返回 > 70 分以上的数学最低分
SELECT MIN(math) FROM exam_result WHERE math > 70;
NULL 和其他数值进行各种运算,结果都是NULL,但是 SUM 比较特殊,会自动跳过NULL的运算。
4.2 GROUP BY
在GROUP BY子句中只能使用列名,不能使用聚合函数。
• 准备测试表及数据
create table emp(
id int primary key auto_increment,
name varchar(20) not null,
role varchar(20) not null,
salary numeric(11,2)
);
insert into emp(name, role, salary) values
('马云','服务员', 1000.20),
('马化腾','游戏陪玩', 2000.99),
('孙悟空','游戏角色', 999.11),
('猪无能','游戏角色', 333.5),
('沙和尚','游戏角色', 700.33),
('隔壁老王','董事长', 12000.66);
• 查询
select role,max(salary),min(salary),avg(salary) from emp group by role;
结果如下:
4.3 HAVING
GROUP BY 子句进行分组以后,需要对分组结果再进行条件过滤时,不能使用 WHERE 语句,而需要用HAVING。WHERE是在分组前过滤数据,而HAVING是在分组后对聚合结果进行过滤。
select role,max(salary),min(salary),avg(salary) from emp group by role
having avg(salary)<1500;
结果如下:
4.4 联合查询
实际开发中数据往往来自不用表,所以需要多表联合查询。多表查询是对多张表提取笛卡尔积
笛卡尔积:在 SQL 中, 多表查询的笛卡尔积是指两个或多个表进行关联查询时,未指定连接条件(如 ON 或 WHERE),导致每个表的每一行都与另一个表的每一行组合,产生一个巨大的结果集。
-- 隐式笛卡尔积(不推荐)
SELECT * FROM table1, table2;
-- 显式笛卡尔积(使用 CROSS JOIN)
SELECT * FROM table1 CROSS JOIN table2;
这里不推荐隐式笛卡尔积,数据库优化器可能无法对隐式笛卡尔积进行有效优化,而显式CROSS JOIN 会提示优化器提前处理全组合逻辑。
我们可以使用 INNER JOIN、LEFT JOIN 等,搭配 ON 子句来指定连接条件,进而避免出现意外的笛卡尔积。
4.5 内连接
在 SQL 中,内连接(INNER JOIN) 是最常用的关联查询方式,用于仅返回两个表中满足连接条件的匹配行。
4.5.1 内连接的核心概念
• 作用:筛选两个表中的交集部分的数据
• 结果集:仅保留满足链接条件的行,不匹配的行被删除
4.5.2 内连接的语法
(1) 显示 INNER JOIN 语法
SELECT 列名
FROM 表1
INNER JOIN 表2 ON 表1.列 = 表2.列;
• 特点:
使用 INNER JOIN 关键字明确关联逻辑,通过 ON指定连接条件,代码可读性强
(2) 隐式内连接语法
SELECT 列名
FROM 表1, 表2
WHERE 表1.列 = 表2.列;
• 特点:
使用逗号多表分隔,通过 WHERE 子句过滤,容易与笛卡尔积混淆,可读性差
4.5.3 ON 与 WHERE 的区别
• ON 子句定义连接条件 (那些行需要关联)
• WHERE 子句在关联后过滤结果 (那些行需要保留)
• 举例:
-- 查询 IT 部门的员工
SELECT e.name, d.dept_name
FROM employees e
INNER JOIN departments d
ON e.dept_id = d.id
AND d.dept_name = 'IT'; -- 在 ON 中过滤更高效
4.6 自连接
4.6.1 定义
自连接是指将同一个表与自身关联,用于处理表中的层级关系或递归结构。通过为表设置不同的别名 (Alias) 来区分逻辑上的两个"表"。
SELECT
a.column1, b.column2 -- 选择需要的列
FROM
table_name a -- 表的第一个别名
JOIN
table_name b -- 表的第二个别名
ON
a.common_column = b.common_column -- 连接条件
[WHERE ...]; -- 可选过滤条件
同一张表列与列之间的比较可以用条件筛选,如果同一张表想行与行之间比较,就需要自连接来查询,创建两张基于原表的"虚拟表"。
4.6.2 核心用途
• 层级关系:如员工与经理 (同一表中)
• 递归结构 (如分类树中的父子节点)
• 同一个表中的数据对比 (如查找同一个客户的多个订单)
4.7 外连接
4.7.1 定义
外连接用于返回两个表中满足条件的行,并保留至少一个表的所有行 (即使无匹配)
• 左外连接 (LEFT OUTER JOIN):保留左表所有行
SELECT
a.column1, b.column2
FROM
table_a a
LEFT JOIN
table_b b
ON
a.key = b.key
[WHERE ...];
• 右外连接 (RIGHT OUTER JOIN):保留右表所有行
SELECT
a.column1, b.column2
FROM
table_a a
RIGHT JOIN
table_b b
ON
a.key = b.key
[WHERE ...];
• 全外连接 (FULL OUTER JOIN):保留左右表所行
SELECT
a.column1, b.column2
FROM
table_a a
FULL JOIN
table_b b
ON
a.key = b.key
[WHERE ...];
4.7.2 核心用途
• 保留未匹配的数据:如查找没有订单的客户、没有客户的订单
• 数据完整性分析:识别缺失或孤立的记录
4.8 子查询
4.8.1 子查询概念
子查询是指嵌入在其他 sql 语句中的 select 语句,也叫嵌套查询
• 单行子查询:返回 一行一列 的结果,通常与单值比较操作符(如=, <, > 等)配合使用。
SELECT column1, column2
FROM table_name
WHERE column_operator (SELECT single_value_column FROM ... [WHERE条件]);
• 多行子查询:返回 多行一列 的结果, 不能使用单值比较符操作,可以使用 in,any,all,exists 来查询。
单行子查询和多行子查询对比
4.9 合并查询
对于不同表不能使用 or 的情况下,就可以使用合并查询。在实际应用中,为了合并多个select的执行结果,可以使用集合操作符 union,union all。使用UNION 和UNION ALL时,前后查询的结果集中,字段需要一致。
SELECT column1, column2, ... FROM table1
[UNION | UNION ALL]
SELECT column1, column2, ... FROM table2
[UNION | UNION ALL]
SELECT ...;
• UNION:该操作符用于取得两个结果集并合并,自动去除结果集中重复的行
• UNION ALL:该操作符用于取得两个结果集并合并,不会自动去除结果集中重复的行
5. 索引
5.1 概念
索引是一种特殊的文件,包含着对数据表里所有记录的引用指针。可以对表中的一列或多列创建索引,并制定索引的类型,各类索引有各自的数据类型。可以理解为书的目录,提高查询速度。
5.2 索引的作用
• 加速查询:通过索引快速定位数据,减少全表扫描
• 约束唯一性:唯一索引确保列值唯一, 一个表只能有一个主键,但是可以有多个唯一索引,主键不能为空,唯一索引允许 NULL(只允许一个)
• 优化排序和分组:索引可加速 ORDER BY 和 GROUP BY 操作
使用场景:数据量较大,且经常对这些列进行条件查询
如果非条件查询列,或者经常有插入,修改操作,或磁盘空间不足时,不考虑创建索引。
5.3 索引的使用
创建主键约束 (PRIMARY KEY)、唯一约束 (UNIQUE)、外键约束 (FOREIGN KEY) 时,会自动创建对应的索引。
• 查看索引
show index from 表名;
• 创建索引
对于非主键、非唯一约束、非外键的字段,可以创建普通索引
create index 索引名 on 表名(字段名);
• 删除索引
drop index 索引名 on 表名;
5.4 B+树
B+树是数据库索引中常用的数据结构,它针对磁盘储存特性优化,支持高效的范围查找,和顺序访问,同时保持稳定的查询性能。
5.4.1 B+ 树的核心特性
• N叉平衡搜索树:每个节点包含多个键和子节点指针,树的高度保持平衡
• 所有的叶子节点位于同一层,保持查询路径长度一致
• 数据仅存储在叶子结点,内部节点仅存储键值和子节点指针,用于导航。叶子结点存储实际数据,并通过链表串联,支持高效的范围遍历。
• 节点容量越大,树的高度越低
5.4.2 B+树的优势
• N叉搜索树,高度低降低了硬盘IO次数
• 范围查询方便且高效
• 所有查询落到叶子结点,开销稳定容易预估成本
• 叶子结点存储数据行,非叶子结点只能存储索引列 key 值,非叶子节点占据空间小,可以加载到内存,进一步减少查询时 IO 次数
6 事务
6.1 概念
事务指逻辑上的一组操作,组成这个操作的各个单元,要么全部成功要么全部失败。在不同的环境中都可以有事务,在数据库中,就是数据库事务。
6.2 事务的 ACID 特性
6.3 事务的隔离级别
6.3.1 READ UNCOMMITTED (读未提交)
现象:事务可以读取其他未提交事务的修改(脏读)
6.3.2 READ COMMITTED(读已提交)
现象:事务只能读取其他已经提交的修改,解决了脏读,但是由于实时性,可能会出现不可重复读。不可重复读指的是,事务A在同一事务内第一次和第二次检查的结果不一样。
读已提交用处
如新闻网站、博客平台、读多写少,对实时性能要求高,允许短暂的数据不一致。
6.3.3 REPEATABLE READ(可重复读,MySQL 默认级别)
现象:确保同一事务内多次读取结果一致,解决不可重复读,但可能出现幻读。可以理解可重复读是时间冻结,在你检查这个事务的时候,结果是不变的,但是提交的时候系统会自动进行修改矫正。
可重复读用处
对数据一致性要求高的事务,比如:
• 银行计算账户利息时,需要基于某一时间点的约来算,期间不受转账影响。
• 生成财务报表时,数据必须基于某个固定时间点
可重复读代价
• 数据库需要保存旧数据快照,占用更多储存空间
• 高并发时可能增加冲突(提交时发现数据已经变化,需要重试)
6.3.4 SERIALIZABLE(串行化)
现象:强制事务串行执行,解决所有并发问题,但是性能最低。系统像"单线程"处理请求,彻底避免开发冲突,但会导致排队等待。
串行化用处
如金融系统、库存扣减、唯一性约束(如用户注册时检查用户名唯一性)、复杂事务逻辑
=========================================================================如果觉得有用的话给博主一键三连吧,祝您在以后的代码之路上越走越远。