MySQL万字超详细笔记???

2024-01-09 19:14:33

MySQL

官网:https://dev.mysql.com/doc/refman/8.0/en/database-use.html
作用:管理逻辑上存在但物理上不存在的数据库和表等大数据环境中的静态资源。
它用于存储结果数据和其他小型数据,避免在大数据环境中存放。
要展示数据成果,只需连接到MySQL即可。
一、特点:
1.属于关系型数据库
常见的关系型数据库:sqlserver,mysql,oracle,db2…
关系数据库的标准:遵循T-SQL语言,以及通过表格结构化存储数据。
它们通过建立表间关系来实现复杂的数据查询和关联。
T-SQL的四部分: DCL(控制)
DDL(定义)
DML(操纵)
DQL(查询)

2.数据
数据以表格形式组织
存在表间关系
每列都有明确的数据类型和约束条件

3.库
系统库:information_schema 存储关于数据库、表、列、索引等结构和元数据信息的数据。
performance_schema 查询执行时间、锁定情况等。
sys 提供一些视图和存储过程
自定义库:
库,类似于文件夹,包含多个表。
表,可比作Excel表,由多个字段组成。每个字段包含名称、数据类型和约束。
对象,代表数据行。
数据,通过对象来表示和存储。

4.语法
;用于分隔一条sql语句,子查询作为一个内部的临时表存在,作为查询的一部分,不能用;结尾。

5.安全性
数据库锁:控制访问和修改数据库中的数据的并发需求。

6.分布式
有分布式存储(可面向集群),无分布式计算

表分区:将一个大表的数据分割成多个较小的部分,每个部分存储在不同的物理位置
分表:将数据分布到多个表中
分库:将数据分布到多个数据库中

二、DCL(创建用户与授权)
在数据库中创建一个自定义表,用于存储外部用户的信息。
向自定义用户表中插入外部用户的信息。
为不同用户分配不同的权限。

1.创建用户:
首先登陆为root用户
创建新用户命令示例:
CREATE USER 'henry'@'%' IDENTIFIED BY '111122';
%表示从任何主机连接,henry是用户名,111122是密码
2.获取当前登录用户的信息:
SELECT CURRENT_USER();可以查看当前登陆的用户和其连接来源
示例:root@localhost即表示是从本地以超级管理员身份登陆的。
3.用户权限控制:
root用户通常只用于本地权限,限制远程访问可增加安全性。
对于需要远程连接的情况,应创建新用户并分配相应权限。
4.远程用户创建及其权限授予:
创建远程用户示例:
CREATE USER 'your_remote_user'@'%' IDENTIFIED BY 'your_password';
授权示例:
GRANT SELECT,INSERT,UPDATE,DELETE ON your_database.* TO 'your_remote_user'@'%';

权限:
	全部
	`ALL PRIVILEGES`
	对象级增删改查
	`CREATE|DROP|ALTER|SHOW`
	数据级增删改查
	`INSERT|DETELE&TRUNCATE|UPDATE|SELECT`
	对象级操作主要关注数据库的结构和架构,而数据级操作关注的是表中存储的具体数据。
	授权一般授予都是数据级操作的权限。
权限的应用范围:
	`ON`关键字用来指定用户在特定主机上对特定数据库或表的权限
	示例:`ON DBNAME.TABLENAME TO USER@HOST`
使权限生效:
	`FLUSH PRIVILEGES;`激活所授权的内容

5.TCL(事务控制语言)
COMMIT表示提交数据,数据被永久保存
ROLLBACK用于数据回滚,可以撤销到最近的一次COMMIT的状态。

COMMIT;
SELECT * FROM TABLE_NAME;		有数据
SET autocommit = FALSE;
DELETE FROM TABLE_NAME;
SELECT * FROM TABLE_NAME;		无数据
ROLLBACK;
SELECT * FROM TABLE_NAME;		有数据

COMMIT后查询会显示数据
设置SET autocommit = FALSE;执行删除操作后,查询将不再显示数据
ROLLBACK;后查询将再次显示数据,显示回滚前的状态。

三、DDL(基本操作)
3.1 命名规则
使用小写字母命名库、表、字段,避免使用SQL关键字。

3.2 库的基本操作
创建库:CREATE DATABASE IF NOT EXISTS DATABASE_NAME;
删除库:DROP DATABASE IF EXISTS DATABASE_NAME;
使用库:USE DATABASE_NAME;
显示库信息:SHOW DATABASES;
查看当前库名:SELECT DATABASE();
修改库:通常涉及删除后再重新创建。
一般不建议改名,以免影响关联内容。主要用于添加约束,如外键。

添加外键约束
ALTER TABLE class   			指定修改的表为class	

ADD CONSTRAINT fk_class_id		添加约束
FOREIGN KEY (fk_class_id)		
REFERENCES class (class_id);	

3.3 表的基本操作
字段
字段定义:字段名+数据类型+约束+字段批注
employee_id INT PRIMARY KEY COMMENT ‘员工id,主键’

创建表
	CREATE TABLE employees (
		id INT PRIMARY KEY,
		first_name VARCHAR(50),
		last_name VARCHAR(50),
		email VARCHAR(100)
	);
		
创建临时表
	WITH t AS(

	);
			
删除表
操作:删除表结构和数据,释放表空间。
命令:DROP TABLE IF EXISTS TABLE_NAME;

清空表:
操作:清空所有数据,保留表结构。
命令:TRUNCATE TABLE TABLE_NAME;

对比 TRUNCATE TABLE 和 DELETE FROM
相同点:都可以实现对表中所有数据的删除,同时保留表结构。
不同点:
		TRUNCATE TABLE:一旦执行此操作,表数据全部清除。同时,数据是不可以回滚的。(原理是在执行TRUNCATE之后立刻执行了一次COMMIT,此时进行回滚是回滚到删除后的状态)
						通常用于测试环境转为生产环境的情况。
		DELETE FROM:一旦执行此操作,表数据可以全部清除。(比较灵活,可以利用WHERE控制删除数据的范围)。同时,数据是可以实现回滚的。(也可以不实现回滚)
					 当你使用DELETE语句删除表中的数据时,数据库管理系统将删除指定的行,但不会重置自增列主键的计数器。(即不会重复使用已经删除的主键值)
阿里开发规范:
【参考】TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但TRUNCATE无事务且不触发TRIGGER(TRIGGER用于一边删一边备份),
有可能造成事故,故不建议在开发代码中使用此语句。建议用DELETE。

显示表:
SHOW TABLE_NAME;

查询表结构:
DESC TABLE_NAME;

【强制】备注:说明表用途
COMMENT "...";

3.4 数据类型
在这里插入图片描述

小结及选择建议:
关于字符串的选择:
任何字段如果为非负数,必须是 UNSIGNED
【强制】小数类型为 DECIMAL,禁止使用 FLOAT 和 DOUBLE。
说明:在存储的时候,FLOAT 和 DOUBLE 都存在精度损失的问题,很可能在比较值的时候,得
到不正确的结果。如果存储的数据范围超过 DECIMAL 的范围,建议将数据拆成整数和小数并 分开存储`。
【强制】如果存储的字符串长度几乎相等,使用 CHAR 定长字符串类型。
【强制】VARCHAR 是可变长字符串,不预先分配存储空间,长度不要超过 5000。如果存储长度大
于此值,定义字段类型为 TEXT,独立出来一张表,用主键来对应,避免影响其它字段索引效率。

3.5 约束
1.数据类型(越小越好)
2.长度(定义数据带长度)
3.是否允许为空:(not) NULL(默认是允许为空的)
4.默认值:default VALUE -> (若没有给提供值,字段的值为默认值)
5.唯一键(唯一、可以为空):unique key
6.主键(唯一、不为空):primary key
主键的作用:主键是用来唯一标识表中的每一行记录的字段,可以用来快速查找和访问特定的记录。
单一和组合主键:单一主键用于唯一标识每一行,而组合主键则使用多个字段的组合来确保唯一性。组合主键通常用于解决多个字段都不足以唯一标识记录的情况。
主键的选择:选取没有实际业务意义的字段作为主键
主键的稳定性:尽量避免主键的变化。

主键和唯一键的区别:
可以有多个唯一键,但是只能有一个主键。
唯一键可能为空,主键不能为空。
主键可以作外键,唯一键不可以。
唯一键是为了限制不受主键约束的列上的数据的唯一性。

7.外键(表间关联):foreign key
外键的作用:外键是表中的一列,它建立了到另一个表中主键或唯一键的关系。
外键约束确保从表中的外键列的值必须存在于主表中的主键或唯一键列中。
主外键一致性:主外键的约束必须一致。
数据完整性:通过检查外键的值是否存在于被引用表的逐渐或者唯一键列中来判断是否拒用正确的关联关系。
从而实现正确的数据库操作。
外键列的值不允许为NULL。

外键与主从表
学生表(从表)中的班级信息必须来源于班级表(主表)中,要先有班级这张表。

外键使得从表中的数据引用主表中的数据的时候,数据的完整性和一致性得到维护。

?主从表
主从表是一种常见的数据库设计模式。
主表(Master Table)通常包含独特的数据,而从表(Slave Table)则包含引用主表主键的外键。
主表和从表之间的关系通常是一对多的关系。一是主表(一个主要实体),而多是主表(多个相关实体)。
在一对一的关系中,主从表可以任意指定。

8.零填充:zerofill
当该字段的值小于定义的长度时,会在该值的前面补上相应的0。
zerofill默认为int(10)
9.自增列:auto_increment
特点:唯一性
自增列必须是主键 ID INT AUTO_INCREMENT PRIMARY KEY

10.无符号数值:unsigned
有符号-128~+127,无符号为0~256

3.6 数据的完整性
数据的完整性是数据库设计的关键部分,它通过各种约束来实现。
数据的完整性包含域完整性,引用完整性和实体完整性三个方面。

     实体完整性:
        概念:确保表中每行数据的唯一性。
   	    手段:主键和唯一键。
			  主键确保表中每行数据的唯一性。
			  唯一键确保在指定列中的所有值都是唯一的。
     域完整性:
	    概念:确保每个字段中的数据值满足一定的条件。
		标准:类型最小化,长度最短化(在确保足够的情况下,预留一些),格式正确。
?     	手段:
			数据类型
			长度限制
			非空约束
			默认值
			自增列
			有/无符号
     引用完整性
?     	概念:确保一个表可以依赖另一个表的值
?     	手段:外键。
			外键确保了一个表中的字段值必须在另一个表的列中有对应值。

DML(数据级)
? 1.增 INSERT
? 可缺省值(有默认值)的字段:自增列主键、允许为空的字段、设定默认值字段。
? 插入数据:
单条或多条数据:INSERT INTO TABLE_NAME(FIELD1,FIELD2,...) VALUES(VALUE1,VALUE2,...) 多个值到单字段表:INSERT INTO TABLE_NAME(FIELD) VALUES(VALUE1),(VALUE2),(VALUE3);`
表间复制:
将A表中的数据复制到B表:
INSERT INTO TABLE_B(FIELD1, FIELD2, …)
SELECT FIELD1, FIELD2, … FROM TABLE_A;;

2.分表
为处理大量数据,根据规则或标准将数据分散到多个表中。
自增列设计:初始值递增,在每个表中自增的间隔相同。
?
? 3.删 DELETE
? 测试环境随便,生产环境禁用。
? 不会删除用户数据,若真要删除用户数据前需要断开关联。
删除单条数据:DELETE FROM TABLE_NAME WHERE CONDITION(基于主键和唯一键)
删除多表连接数据:指定表实例进行删除
?
? 4.改 UPDATE
更新数据:
? UPDATE TABLE_NAME
? SET FIELD1=‘VAL1’,FIELD2=‘VAL2’…FIELDN=‘VALN’
? WHERE CONDITION;(基于主键或唯一键)
注意:执行更新操作时,唯一字段的新值不能与现有值重复。

5.DDL 和 DML 
	DDL操作一旦执行,不可回滚。
	DML操作默认不可回滚,但若执行SET autocommit = FALSE后,可回滚。
	
	事务
		定义:由一到多个DML语句构成
		性质:要么成功要么回滚
		
	MySQL8新特性——DDL的原子化
		定义:DDL操作支持事务完整性,即要么成功要么回滚。
			  改变了DDL操作一旦执行,不可回滚的限制。防止数据库的结构和数据受到部分完成的DDL操作的影响。
		例:
			CREATE DATABASE mytest;
			USE mytest;
			CREATE TABLE book1(
				book_id INT,
				book_nam VARCHAR(255)
			);
			SHOW TABLES;
			DROP TABLE book1,book2;
			SHOW TABLES;
			即本身不存在book2这张表格,由于MySQL8的原子化特性,不完全成功就会进行回滚,
			因此最终结果并不会删除book1这张表格。

DQL
一 简单查询
1.逻辑运算符
and or not
2.简单查询

数据集类型:
	视图:存储SQL语句结果的虚拟表,仅用于查询,不存储实际数据。
	临时表:存储临时数据,数据库会话结束后自动删除。
查询格式:
	关键字换行以提高可读性。
查询语法:
	基本查询:SELECT 字段列表 FROM 表名 WHERE 条件;
	列选择:
		单个字段:SELECT FirstName FROM 表名;
		多字段组合:SELECT FirstName, LastName FROM 表名;
		字段部分:SELECT SUBSTRING(Email, 1, 5) FROM 表名;
	别名:用于简化字段或表的名称,可以省略AS关键字。
	条件查询:SELECT count(*) FROM 表名 WHERE 条件;
	条件语句:
		比较:=, <>, >, <, BETWEEN, IN
		空值判断:IS NULL, IS NOT NULL
		模糊匹配:LIKE,使用%(任意字符)和_(单个字符)作为通配符。
结果排序和限制:
	排序:ORDER BY 字段 [ASC|DESC]
	限制:LIMIT 起始位置, 数量
常见注意事项:
	浮点数值自动去除小数点后的零。
	区分查询的主次关系,例如区分“班级为主的查询”与“科目为主的查询”。  

二 分组(聚合)查询
1.单字段分组: 与多字段分组类似。
2.多字段分组: 使用 GROUP BY A, B 来对每个唯一的 (A, B) 对进行聚合操作。这意味着首先按 A 分组,然后在每个 A 分组内按 B 分组。这与 GROUP BY B, A 不同。
3.窗口分组: 使用 PARTITION BY 在查询结果的子集上执行计算。
4.结果集分组: 使用 GROUP BY 根据一个或多个字段对整个查询结果进行分组,并应用聚合函数(如 SUM, AVG, COUNT)以产生每个分组的单一聚合结果。
5.SELECT 子句: 当使用聚合函数时,SELECT 子句中通常只包含常量、聚合函数或 GROUP BY 指定的列名。
6.WHERE 与 HAVING 的区别:
WHERE: 用于原始数据表,不能使用聚合函数的别名,直接过滤数据。
HAVING: 在 GROUP BY 之后使用,用于过滤经过分组聚合后的数据表。
7.聚合非聚合列
A.为非聚合列添加聚合函数
SELECT MAX(COLUMN1) AS COL1 FROM SOURCE_TABLE;
B.将非聚合列作为分组列
SELECT A,B FROM SOURCE_TABLE GROUP BY A,B

聚合函数	
	纵向最大	MAX(EXP)
	纵向最小	MIN(EXP)
	纵向平均	AVG(EXP)
	纵向求和	SUM(EXP)
	纵向计数	COUNT(* | 1 | (DISTINCT) COL_NAME) 计算非空值数量
	纵向拼接	GROUP_CONCAT([DISTINCT] 要连接的字段 [Order BY ASC/DESC 排序字段] [Separator '分隔符']) 默认用逗号分隔。
	数据汇总	GROUP BY COLUMN1,COLUMN2 WITH ROLLUP;
				用于计算每个分组的结果,和汇总结果。
				
	SELECT year, IFNULL(month,"总体"), SUM(earnings) as total_earnings
	FROM sales
	GROUP BY year, month WITH ROLLUP;
	这段查询不仅提供每个年份下每个月份的收入总额,还会在每个年份的末尾提供该年份的总收入汇总,
	当`month`为NULL时,表示该行是一个汇总行,此时的`month`就会显示为总体(WITH ROLLUP和IFNULL(...,...)配合使用)
?		
	聚合函数可以嵌套使用 SUM(COUNT(*))
	
行列转换 
	
	行转列:多行转成一列
	列转行:一列拆成多行 多列拆成多行

	行式数据库按行存储,列式数据库按列存储;
		列式数据库便于查找一类数据
		列式数据库在行列转换(数据透视)中发挥重要作用

	非透视行列转换
		使用 GROUP_CONCAT 和 CONCAT_WS 函数将每位学生的多个成绩合并为单个字段。
			select
				S.stu_name,
				group_concat(concat_ws(':',U.subject_name,C.score) order by C.score desc) as stu_scores
			from score_info C
			inner join student_info S
			on C.fk_stu_id = S.stu_id
			inner join subject_info U
			on C.fk_subject_id = U.subject_id
			group by S.stu_name;
	
	透视行列转换(将多行数据转化为一行 => 只是针对特定的作为字段的列 => 利用列式数据库)
		select
			S.stu_name,
			(select score from score_info where fk.stu_id = S.stu_id and fk_subject_id=21) as JavaEE
			(select score from score_info where fk.stu_id = S.stu_id and fk_subject_id=22) as Hadoop,
			(select score from score_info where fk.stu_id = S.stu_id and fk_subject_id=23) as Spark,
			(select score from score_info where fk.stu_id = S.stu_id and fk_subject_id=24) as Python,
			(select score from score_info where fk.stu_id = S.stu_id and fk_subject_id=25) as Project

	变相分组
		select
			S.stu_name,
			max(if(U,subject_name='JavaEE',score,0)) as JavaEE,
			max(if(U,subject_name='Hadoop',score,0)) as Hadoop,
			max(if(U,subject_name='Spark',score,0)) as Spark,
			max(if(U,subject_name='Python',score,0)) as Python,
			max(if(U,subject_name='Project',score,0)) as Project
子查询
	子查询别名的使用:
		FROM 子句中的子查询: 必须有别名,因为子查询在这里充当一个临时表,在主查询中作为表使用。
		SELECT, HAVING, WHERE 子句中的子查询: 一般返回单个值,通常不需要别名。但如果子查询的结果作为一列出现在 SELECT 子句中,通常需要别名以便于引用。
	子查询的应用场景:
		SELECT 子句: 作为计算字段的一部分,例如 (SELECT COUNT(*) FROM table) AS total。
		FROM 子句: 作为临时表,例如 FROM (SELECT * FROM table) AS temp。
		WHERE 子句: 用于存在性检查,例如 WHERE EXISTS (SELECT * FROM table WHERE condition)。
		HAVING 子句: 用于对聚合结果进行过滤。
		ORDER BY 和 LIMIT 子句: 用于对查询结果进行排序和限制。

获取在LogicJava和javascript中取得前十名的学生信息和分数
	select *
	from (	
		select
			fk_talent_id,score
		from (
			select fk_talent_id,score
			from yb_score
			where subject in ('LogicJava','javascript')
		) as ALIA order by score desc
		limit 0,10;
	)
	
获取LogicJava和javascript的同时出现在前二十名的学生信息和分数
解题思路:利用窗口函数
select fk_talent_id
from yb_score A
inner join (
	select subject,score 
	from(
		select subject,score,dense_rank() over(
			partition by subject order by score desc range between unbounded preceding and unbounded following 
		) as rnk
		from yb_score
		where subject in ('logicJava','javascript')
	) T 
	where rnk = 20;
)B on A.score>= B.score and A.subject = B.subject
group by fk_talent_id
having count(*) = 2;
?	解题思路:获取两个科目第二十名学生的分数,科目,排名信息;将其与yb_score关联,并获取高于二十名分数的学生信息。
?		select 
?			fk_talent_id
?		from yb_score A
?		inner join(
?			select
?				subject,score,lj_rank,js_rank
?			from(	
?				select 
?					A.subject,A.score,
?					if(A.subject='LogicJava',@lj:=@lj+1,0)  lj_rank,
?					if(A.subject='javascript',@js:=@js+1,0) js_rank
?				from(
?					select
?						subject,score
?					from yb_score
?					where subject in ('LogicJava','javascript')
?					group by subject,score
?					order by subject,score desc
?				)A,(select @lj:=0,@js:=0)B	=> 子查询并不一定需要获取数据,也可以执行一些赋值操作。
?			)A
?			where subject='LogicJava' and lj_rank = 20
?			or subject='javascript' and js_rank = 20
?		)B on A.subject=B.subject and A.score>=B.score
?		group by fk_talent_id
?		having count(*)=2;
?		
		select subject,score
		from yb_score
		where subject in ('logicJava','javascript')  => 获取两个科目对应的倒序排名
		group by subject,score
		order by subject,score desc;


?		
?		select
?			A.subject,A.score
?			if(A.subject='logicJava',@lj:=@lj+1,0) lj_rank,
?			if(A.subject='javascript',@js:=@js+1,0) js_rank  => 声明并初始化@lj和@js,给其计数
?		from(
?			...
?		)A,(select @lj:=0,@js:=0)B	=> B表子查询仅作声明
?	
		select
			subject,score,lj_rank,js_rank
		from(												=> 获取两个科目第二十名学生的分数				
			...
		)
		where subject = 'logicJava' and lj_rank = 20
		or subject='javascript' and js_rank = 20
		
		select 
			fk_talent_id
		from yb_score A
		inner join(
			...												=> 获取大于第二十名分数的同时在两个科目的学生信息		
		)B on A.subject=B.subject and A.score>=B.score      => 此时的B相当于一个临时的表格,便于做数据筛选
		group by fk_talent_id
		having count(*)=2;


?			
?	select * from 可以用于对内部查询的结果进一步添加操作 => 内部查询结果进行进一步的操作时,例如再次过滤、排序,或者与其他的查询结果进行联接等。
?		
	数据表可以由子查询代替	
	select 
		fk_talent_id, count(*) as score_count
	from(
		//倒叙排 锁定前三十
		select
			fk_talent_id,score
		from(
		//两个科目
			select fk_talent_id,score
			from yb_score
			where subject in ('logicJava','javascript')
		)T order by score desc 
		limit 0,30
	)T group by fk_talent_id
	having score_count=2;
	       
	查询条件也可以是子查询
	select subject, score
	from yb_score
	where exists (select * from yb_score where subject='oop');
	select * 
	from yb_score
	where subject in('oop','javascript') 
	and fk_talent_id in (
		 select talent_id from yb_talents
		 where talent_school in('南林','南邮')
	);

连接查询(多表联合查询)
ON 和 USING 的异同:
ON A.COLUMN_NAME = B.fk_COLUMN_NAME 等同于 USING(COLUMN_NAME)
不能直接在USING子句中添加额外条件,但在ON子句中可以添加额外条件

	WHERE 和 AND 的异同:
		WHERE
			SELECT e.emp_no, s.to_date
			FROM employees e
			LEFT JOIN salaries s ON e.emp_no = s.emp_no
			WHERE s.to_date = '9999-01-01';
		AND
			SELECT e.emp_no, s.to_date
			FROM employees e
			LEFT JOIN salaries s ON e.emp_no = s.emp_no 
			AND s.to_date = '9999-01-01';
			
	`WHERE`的顺序是先关联再过滤,先进行LEFT JOIN,然后从结果集中过滤出`to_date`为'9999-01-01'的行。
	`AND`的顺序是在JOIN操作的时候就考虑了`to_date`条件,在左表中存在但是`to_date`不是'9999-01-01'的员工会显示为NULL。
	
	`WHERE`更适合筛选出`完全符合条件`的行,忽略不符合条件的数据。
	`AND`更适合保持关联查询中主表数据的完整性。
   
	数据库表的非传统关系:
	
		1.自引用关系:表中的记录与同一表中的其他记录存在关联。例如,一个员工和其他员工之间可能存在着上下级关系。
		CREATE TABLE Employees (
			employee_id INT PRIMARY KEY,
			name VARCHAR(255),
			manager_id INT, -- 指向上级员工的外键
			FOREIGN KEY (manager_id) REFERENCES Employees(employee_id)
		);
		
		2.嵌套关系
		CREATE TABLE Articles (
			article_id INT PRIMARY KEY,
			title VARCHAR(255),
			comments JSON -- 嵌套的评论数据
		);
	
		-- 示例数据
		INSERT INTO Articles (article_id, title, comments)
		VALUES (1, '文章标题', '[{"comment": "很有见地的文章!", "author": "Alice", "timestamp": "2023-09-04 10:00:00"},
							   {"comment": "我完全同意!", "author": "Bob", "timestamp": "2023-09-04 10:30:00"}]');

? 多表查询的意义:
? “每个xx的xx”:内连接订单表和产品表,可以查看每个订单的具体产品信息。
? “关联数据”:将不同表中的相关数据关联起来,构建满足需求|完整的信息视图。
? 同时可以将不同表中的数据进行组合。
? “数据筛选”:便于进行基于"跨表条件"的数据筛选,减少冗余数据,排除无用数据。
?
多个数据来源
"FROM t1,t2"并未构建特定的表连接关系。

	多对多关系:
	两个表之间如果进行交叉关联(存在多对多关系),关联点的维度需要是一致的。
	// 书籍表
	CREATE TABLE Books (
		book_id INT PRIMARY KEY,
		title VARCHAR(255)
	);

	// 作者表
	CREATE TABLE Authors (
		author_id INT PRIMARY KEY,
		name VARCHAR(255)
	);

	// 作者-书籍关联表
	CREATE TABLE Author_Book (
		author_id INT,
		book_id INT,
		PRIMARY KEY (author_id, book_id),
		FOREIGN KEY (author_id) REFERENCES Authors(author_id),
		FOREIGN KEY (book_id) REFERENCES Books(book_id)
	);

?
? 笛卡尔积 cross join
定义:不需要任何连接条件,将A表中的每一行分别与B表中的每一行进行匹配合并。
? 原因:1.关联点并非完全匹配
? 2.两表内连接非唯一字段
? 3.两表左外连接非唯一字段
?
后果:出现大量不合理数据
解决:直接进行等值关联
场景:通常用于没有明确的关联关系,但是需要两个表的所有记录进行组合的情况。例如足球比赛排期。

	内连接(交集) (inner) join
	定义:筛选出两表中符合条件的记录(交集)。

	当多行连续进行内连接的时候,示例如下:
	inner join stu_class_relation scr on C.fk_stu_id=scr.fk_stu_id
    inner join class_info A on A.class_id=scr.fk_class_id
    inner join subject_info J on J.subject_id=C.fk_subject_id	
	其实是有执行顺序的,两张表先"合并"为一张表,再在这张表的基础上继续进行"合并",不管是逻辑关联还是物理关联,只要关联点是在同一个维度上,都能起到约束作用。
		
	外连接(全集/差集)
	定义:保留主表中的所有记录,以及从表中与主表相符的记录,从表中与主表不符的记录用NULL表示。
		左外连接 left join
		from 主 left join 从
		右外连接 right join
		from 从 right join 主
		
	题目特征:
		所有学生的课程成绩:学生表作为外连接的主表

		
	补充:全外连接
	定义:本质是为了合并数据,返回在两表中所有的行,只在一个表中有的数据行缺的部分用NULL填充。
	主外键关系的多表连接查询不适用全外连接的原因:
	1.会提供一些不符合逻辑|没有必要的数据:没有订单的客户,没有客户的订单
	2.如果在Orders表中出现了不存在的客户的数据,左|右外连接可以筛选掉这些不合理的数据,而全外连接在合并数据的时候无法筛选掉这些不合理的数据。
		
       练习题:
       		1.每个【部门】各有多少名【员工】
       		select 
       			D.dept_name,count(*) as emp_count
       		from dept_info D
       		left join post_info P
			on D.dept_id=P.fk_dept_id
			left join emp_info E
			on P.post_id=E.fk_post_id
			group by D.dept_id;
			2.带班最多的老师
			A、通过名词,已经名词的分析
				班:class_info
				老师:post_info + emp_info
				关系:emp_class_relation
			B、通过名词的关系,确定SQL语句的结构
				1、查老师-子查询
				select E.emp_id,E.emp_name
				from post_info P
				inner join emp_info E
				on P.post_id=E.fk_post_id
				and P.fk_dept_id=1;
				2、以查老师作为子查询,关联班级、班级关系表
				create view v_teacher_class
				as
                    select
                        T.emp_name,count(*) as class_count
                    from(
                        select E.emp_id,E.emp_name
                        from post_info P
                        inner join emp_info E
                        on P.post_id=E.fk_post_id
                        and P.fk_dept_id=1
                    )T left join emp_class_relation R
                    on T.emp_id=R.fk_emp_id
                    left join class_info C
                    on C.class_id=R.fk_class_id
                    group by T.emp_id;
                    
                 create temporary table t_teacher_class(
                        select
                            T.emp_name,count(*) as class_count
                        from(
                            select E.emp_id,E.emp_name
                            from post_info P
                            inner join emp_info E
                            on P.post_id=E.fk_post_id
                            and P.fk_dept_id=1
                        )T left join emp_class_relation R
                        on T.emp_id=R.fk_emp_id
                        left join class_info C
                        on C.class_id=R.fk_class_id
                        group by T.emp_id
                 );   
                    
                 select A.emp_name
                 from v_teacher_class A
                 inner join(
                 	select max(class_count) as max_count 
                    from v_teacher_class
                 )B on A.class_count=B.max_count;
                    
                 select emp_name
                 from v_teacher_class
                 where class_count=(
                     select max(class_count) as max_count 
                     from v_teacher_class
                 );
                 
	//
			select 
			
			from yb_score S
			left join yb_talents T
			on S.id=T.id
			group by subject

函数

判断空值
						ifnull(N,M) => 如果N为空,那么返回M,不为空则返回N本身。
数学函数
	+-		符号		sign(N) => 1/-1,1表示正值,-1表示负值
			绝对值		abs(N) => N/-N
			
	规约	向上取整 	ceil(M.N) 			=> M>0 ? M+1:M;
			向下取整	floor(M.N)			=> M>0 ? M:M-1;
			四舍五入	round(D[,N]) 		=> round(D) => 不保留小数,看第一位小数的值
											=> round(D,N) => 保留N位小数,看第N+1位小数的值
			格式化保留	format(D,N)	 		=> format(123456.789,2) => 123,456.789 将数字标准格式化之后保留N位小数
			保留精度    truncate(D,N)		=> truncate(123.456,2) => 123.45
				
	指对	自然幂运算  exp(N)	        	=> 2.718...^N(N可为小数)
			幂运算		pow(B,N)			=> B^N
			自然对数	ln(N)				=> ln(exp(2)) => 2
			对数		log(B,N)			=> log(2,2^2) => 2
			
	常用数	横向最大 	greatest(V1,V2,...) => 返回参数列表中的最大值
			横向最小	least(V1,V2,...)    => 返回参数列表中的最小值
			随机数		rand()				=> 返回0~1之间的随机小数(不包含0,1)
			伪随机		rand(seed)			=> 伪随机数:每次传入相同的种子获取的都是相同的系列数(是通过特定的算法获取的)
			进制转换	conv(N,fromBase,toBase) => 进制:2,8,10,16
	
字符串函数
			ASCII码		ascii(char(1))		=> 返回字符的ASCII码(若传入字符串,则取第一个字符的ASCII码)
			字节长度	length(char(n))		=> 返回字符串字节长度(utf8mb4:三字节汉字)
			字符长度	char_length((char(n)))	=> 返回字符串字符长度
			
			拼接字符串	concat(char(n),int(4),decimal(3)) => 没有类型限制
			连字符拼接	concat_ws(separator,f1,f2...) 	=> 带分隔符的拼接
			
			找位置		FIND_IN_SET(SON,FATHER(逗号分隔)) => 利用逗号分隔的数字模拟string list,返回SON在FATHER中的下标(从1开始,不存在则返回0)
						LOCATE(SON,FATHER(不用逗号分隔)) => 返回FATHER中下标从1开始的第一个SON的第一个字符的下标(从1开始,不存在则返回0)
						LOCATE(SON,FATHER,POS) => 返回FATHER中下标从POS开始的第一个SON的第一个字符的下标
						POSITION(SON IN FATHER) => 返回FATHER中下标从1开始的第一个SON的第一个字符的下标(从1开始,不存在则返回0)
						SUBSTRING_INDEX('xxx,xxx,xxx,xxx',',',n) => 获取前n个数值(从n开始) => 获取第n个数值 SUBSTRING_INDEX(SUBSTRING_INDEX('xxx,xxx,xxx,xxx',',',n),',',N)
						select find_int_set('ab','xyzx,ab,cd') => 2
							   locate('ab','xyzxabcd') => 5
							   substring_index('ab,cd,efg,jk,lm',',',2) => ab,cd
			正则		regexp '...'		
			去除两端空格[l|r]trim(str)
			大小写转换	lower() upper()
			大小写比较	strcmp(stra,strb) => a>b? 1:a=b?0:-1;
			颠倒		reverse(str) => reverse('abc') => cba
			重复		repeat(str,times) => repeat('abc',3) => abcabcabc
			替换		replace(FILED_NAME,old_str,new_str)
						regexp_replace(source, pattern, replace_string)
						- source表示原始的字符串,pattern表示正则表达式,replace_string表示将正则匹配的部分替换为对应的字符串
						eg:获取'ab,cd,efg,hi'的数块数
						regexp_replace(source, '[^,]', ',')+1
			子串截取(下标从1开始,长度包括fromPos的字母)	substr(str,fromPos,len) => substr('abcdef',4) => def
												            substr('abcdef',4,2) => de
						left(str,len) 从左开始截取字符串,len是截取的长度
						right(str,len) 从第二个字母开始截取到字符串末尾,len是截取的长度
			加密(对用户的信息进行脱敏,直接拿加密的字段进行计算即可)		
						非对称加密	只可以加密,并且对同一个对象加密结果相同。
						md5(concat('prefix_salt','YOUR_PASSWORD','suffix_salt'));
						
						对称加密	既可以加密也可以解密。
						加密:select hex(aes_encrypt('123abc','yb12211'));
						=> D58445C73CFBE270BCA227014ACB9870
						解密:select cast(aes_decrypt(unhex('D58445C73CFBE270BCA227014ACB9870'),'yb12211') as char);
						=> 123abc
						hex/unhex转化为16进制数之后,再通过cast的类型转化使其变为字符。
						cast ... as decimal/char(不可cast为int)
日期函数	
			在数据库中,日期格式的字符串可以等同于日期
			当前日期 	   		current_date() => 2023-07-25
			当前时间 	   		current_time() => 11:58:42
			当前日期和时间 		now()/current_timestamp(); => 2023-07-25 11:59:15
			当前时间戳	   		unix_timestamp(DATE); => 返回当前系统时间的时间戳(不传参数)/参数日期的时间戳(以s为单位)
			将时间戳转化为日期	from_unixtime(unix_timestamp(DATE)); => 返回长整数对应的日期格式(必须提供参数)
			日期提取 			extract(year|quarter|day|hour|minute|second from now());
								year(now()),month(now())... 只能用于完整日期的提取
								date_format(trans_date,'%Y-%m-%d') => 以特定形式提取日期:获取完整日期的'年-月'
								非完整日期的提取例如'2026-02'通常使用字符串函数
								RIGHT(s,n)	返回字符串 s 的后 n 个字符
								LEFT(s,n)	返回字符串 s 的前 n 个字符
								MID(s,n,len)	从字符串 s 的 n 位置截取长度为 len 的子字符串,同 SUBSTRING(s,n,len)
								LCASE(s)	将字符串 s 的所有字母变成小写字母
								
			日期判定			date like '2022-02%' 表示22年2月份的所有日期
			周日				dayofweek(now()); => 日-1 一-六2-7
				区分工作日和周末
					sum(dayofweek(submit_date) not in (1,7)) as weekend_cnt,
					sum(dayofweek(submit_date) in (1,7)) as working_cnt
			周日				weekday(now()); => 一-日 0-6
			月日				day|dayofmonth(date); => 月的第几天
			年日    			dayofyear(date) => 年的第几天
			年周				weekofyear(now()); => 年的第几周
			季周	            
				获取当前季度的第一天日期:SELECT CONCAT(DATE_FORMAT(LAST_DAY(MAKEDATE(EXTRACT(YEAR FROM  CURDATE()),1) + INTERVAL QUARTER(CURDATE())*3-3 MONTH),'%Y-%m-'),'01');   
				获取当前日期与第一天日期的天数差并且除7+1
			月周
				获取当前月份的第一天日期:SELECT CONCAT(DATE_FORMAT(LAST_DAY(CURRENT_DATE()),'%Y-%m-'),'01'); 
				获取当前日期与第一天日期的天数差并且除7+1
		    日期计算  			adddate(date,interval(N UNIT));
								timestampadd(UNIT,num,date|datetime);
								
			日期天数差异	    timestampdiff(UNIT,fromDate|fromDatetime,toDate|toDateTime)
			(注意日期不能相减)	timestampdiff是后面减前面的,datediff是前面减后面的。
"EXPLAIN":查询语句的执行计划,以便了解查询的性能和优化方式。
EXPLAIN SELECT * FROM score_info;

id: 查询的标识符。如果一个查询包含了子查询,那么主查询的id为1,子查询的id会递增。
select_type: 查询的类型。可能的值有:SIMPLE(简单查询,没有子查询或者union),PRIMARY(查询中最外层的查询),SUBQUERY(在select或者where子句中的子查询),DERIVED(在from子句中的子查询)等。
table: 输出结果集的表的名称。
partitions: 匹配的分区。
type: 表连接类型。最好的是system和const,最差的是ALL。
possible_keys: 表示可能应用在这张表中的索引。如果为空,那么没有可能的索引。
key: 实际选择的索引。如果为NULL,那么没有使用索引。
key_len: 使用的索引的长度。在不损失精确性的情况下,长度越短越好。
ref: 显示索引的哪一列被用于查找,可能的值有:一个常数,一个字段名或者一个函数。
rows: MySQL认为必须检查的用来找到所需行的行数。
filtered: 表示返回结果集的行占需要读到的行(rows列)的百分比。
Extra: 包含MySQL解决查询的一些额外信息。例如,使用了哪种类型的查询优化。

“DESC”:获取表的结构信息
DESC TABLENAME;
	索引
		概述:索引是数据库查询加速的一种数据结构,类似于书的目录。它基于排序结构,支持高效查找,优化查询条件的搜索速度。
			  
	    创建索引:'CREATE INDEX'
		CREATE INDEX ix_product_description ON products(product_description);

?		删除索引:'DROP INDEX'
?		DROP INDEX IX_NAME ON TABLE_NAME;
?		
		查表索引:'SHOW INDEX'
		SHOW INDEX FROM TABLE_NAME;
		
		索引分类:
			聚簇索引:用于主键,决定了数据在物理存储上的顺序。
			非聚簇索引:包括普通索引、唯一索引、组合索引等。
		
		适用场景:适用于频繁查询和大数据量的情况。
		
		优点:
			提升数据检索速度。 
			增强多表连接查询的效率。
			  
		缺点:
			占用额外物理空间,影响数据存储。
			在数据频繁插入或删除时,维护索引的开销较大。
			索引的创建和维护随数据量增长而耗时增加。
			  
		不宜使用索引的场景
			1.不常使用的列
			2.数据值很少或重复值很多的列
			3.包含大量NULL值的列
			4.LIKE语句中左侧有'%'时(LIKE '张%' ? LIKE '%张%' ?)
			5.表连接字段
			6.唯一性差的字段
			7.频繁更新的字段 
			8.选择性高的字段
		
		本质:
			索引基于B树或B+树实现,用于存储表中的关键字(索引列值)和指向实际数据行的位置或指针。
			关键字有序存储(升序或降序)。
			聚集索引包含指向实际数据物理地址的指针,非聚集索引含主键值,需通过此主键值找到聚集索引(回表)。
		
		形式:
			索引占用表空间,用于存储和管理数据库数据及索引。
		
		优化:
			在长字符串字段上创建索引时,使用定长字符串作为索引可以提高效率。
变量
	变量类型:
		用户级变量(用@标识):主要在远程主机上使用
		全局变量(用@@或global标识):在虚拟机中使用,为所有用户共享
		局部变量:无特殊符号,应用于函数或存储过程中
	使用场景:
		查询操作:通常编写在存储过程中
		增删改操作:编写在函数中
		分支逻辑:出现在函数和存储过程中
	操作方法:
		设置用户级变量:
			示例:`set @val=0;`(不进行逻辑判断)
			累加操作:`set @val = @val + 2;`
			设置并赋值:`select @val:=11;`(若不加冒号,执行的是逻辑判断)
			查看变量值:`select @val;`
		设置全局变量:
			示例:`set @@val = 5;`
			查看特定全局变量:`show variables like '%character_set%';(用于显示包含特定字符的系统变量,如字符集设置)`
		局部变量的使用:
			原则:在函数头部声明所有变量
			声明方法:`declare var_name var_type default VALUE`
			示例:`declare var_1 int(8) default 5`
分支
	空值函数(`nullif`):
		用途:用于处理可能出现的空值
		语法:`nullif(V1,V2);`,当`V1=V2`时返回`NULL`,否则返回V1
	
	简单分支(三元运算符):
		用途:基于条件选择两个值中的一个。
		语法:if(CONDITION, v1, v2);如果CONDITION为真,则结果为v1,否则为v2。
		注意:在v1或v2处可以嵌套另一个if语句。
	
	条件符号:
		=:用作等于操作符。
	
多分支(case-when-else-end):
	形式一:基于表达式的多值选择。
	语法:case EXP when V1 then do... when V2 then do... else do... end;
	形式二:基于条件的多值选择。
	语法:case when CONDITION1 then v1 when CONDITION2 then v2 else v3 end;
	注意事项:确保条件顺序正确,以避免逻辑错误。例如,当有多个条件关联时,应先检查更具体或更紧急的条件。

多分支(if-elif-else-end if):
	用途:处理多个条件,每个条件对应不同的操作。
	语法:if CONDITION1 then do... elif CONDITION2 then do... else do... end if;
函数
	定义
		用途:主要用于增删改过程中。
		命名规则:通常在函数名前添加一个固定前缀。
	函数体
		特点:一般不使用@变量,以分号(';')结尾。
		声明函数时无需提交,但在调用函数时需要提交。
	创建函数
		语法
			create function FUNC_NAME(arg_type,arg_name) returns type;
		修改结束符
			delimiter //
		示例:
			delimiter //
				create function test(a int,b int) returns int
				determinstic 
				begin
					declare rst int default 0;
					if b=0 then set rst=NULL;
					else set rst=a/b;       函数体
					end if;
					return rst;
				end //
			delimiter ;
			注:`determinstic`关键字表示函数执行具有确定的结果,有利于缓存和优化。
    查看函数:
		状态查询:`show function status like 'FUNC_NAME%';`
		+------+-------+----------+----------------+---------------------+---------------------+---------------+---------+----------------------+----------------------+--------------------+
		| Db   | Name  | Type     | Definer        | Modified            | Created             | Security_type | Comment | character_set_client | collation_connection | Database Collation |
		+------+-------+----------+----------------+---------------------+---------------------+---------------+---------+----------------------+----------------------+--------------------+
		| test | hello | FUNCTION | root@localhost | 2014-11-21 14:48:05 | 2014-11-21 14:48:05 | DEFINER       |         | latin1               | latin1_swedish_ci    | latin1_swedish_ci  |
		+------+-------+----------+----------------+---------------------+---------------------+---------------+---------+----------------------+----------------------+--------------------+
		查看函数信息:`show create function FUNC_NAME`;
	调用函数:
		`select func1(args...);`
		`select count(*) as count;`
	删除函数:
		`drop function if exists FUNC_NAME;`
	设置函数创建信任
		用于允许用户创建函数:`set global log_bin_trust_function_creators=TRUE;`
存储过程
定义:预编译代码块,存储于数据库,用于重复调用。
参数:
	`in` 输入参数  
	`out` 输出参数  
	`inout` 输入/输出参数 可读取和修改
删除存储过程
	drop procedure if exists PROCEDURE_NAME
创建存储过程 create
	create procedure PROCEDURE_NAME(args...);
调用存储过程
	call PROCEDURE_NAME(args...);
	
示例:
	定义一个分页查询的存储过程:
	drop procedure if exists pro_find_stu_by_page;
	delimiter //
	/*
	 输入参数:想要查询的页码,一页的数据量
	 输出参数:总页码
	 */
	create procedure pro_find_stu_by_page(
		in pageNo int,
		in pageSize int
	)
	# fromOffSet:对应页码的第一条数据的下标
	# 先获取合法的pageNo
	begin
		declare fromOffSet int default 0;
		declare pageTotal int default (
			select ceil(count(*)/pageSize) from student_info
	   );
		set pageNo = (
			case
				when pageNo<1 then 1
				when pageNo>pageTotal then pageTotal
				else pageNo
			end
		);
		set fromOffSet = (pageNo-1)*pageSize;
	#     无需使用输出参数,而是通过select生成两个结果集。
		select pageTotal;
		select
			 stu_id,
			 stu_name,
			 stu_pid,
			 stu_gender,
			 add_province,
			 add_city,
			 stu_diploma,
			 stu_type,
			 stu_major
		from student_info
		limit fromOffSet,pageSize;
	end //
	delimiter ;
	call pro_find_stu_by_page(30,20);
循环
	死循环(`loop`)
		NAME:loop 创建循环
		leave NAME 跳出死循环
		iterate NAME 继续死循环
		
		示例:		
			drop function if exists func2;
			delimiter //
				create function f2(endSum int) returns int;
				determinstic
				begin
					declare i int default 1;
					declare proSum int default 0;
					MY_LOOP : loop
						set proSum = proSum + i;
						if proSum>=endSum then leave MY_LOOP;
						end if;
						set i = i + 1;
					end loop;
					return i;
				end//
			delimiter ;
	
	先判断后执行(`while`)?
	示例:
		drop function if exists func2;
		delimiter //
			create function f2(endSum int) returns int;
			determinstic
			begin
				declare i int default 0;
				declare proSum int default 0;
				while proSum < endSum do
					set i = i + 1;
					set proSum = proSum + i;
				end while;
				return i;
			end //
		delimiter;
	 
	先执行后判断(`repeat`) 
	示例:
		drop function if exists func2;
		delimiter //
			create function f2(endSum int) returns int;
			determinstic
			begin
				declare i int default 0;
				declare proSum int default 0;
				repeat
					set i = i + 1;
					set proSum = proSum + i;
				until proSum >= endSum
				end repeat;
			end//
		delimiter;
	
	区别:
		`while`循环在执行前判断条件,适用于可能不执行的场景。
		`repeat`循环至少执行一次,适用于至少需要一次执行的场景。
		`loop`提供更灵活的控制,通过 leave 明确指定退出点。

?
?

联合查询 
?		用途:扩展行
?		要求:字段数量和类型必须一致,字段名以第一张表为准。
		类型:
?			`UNION`:去重、排序、效率低。
?			`UNIONALL`:不去重,不排序,效率高。
?	列转行	
?		目的:将多列数据转换成行形式。
?		应用示例:
			将课程名和分数从多列变为单列
				SELECT stu_name, JavaEE AS score, 'JavaEE' AS subject FROM temp_wide_score
				UNION ALL
				SELECT stu_name, Hadoop AS score, 'Hadoop' AS subject FROM temp_wide_score
				UNION ALL
				...
			对每个科目分组,按分数降序排序
				SELECT 
					subject,
					GROUP_CONCAT(DISTINCT score ORDER BY score DESC SEPARATOR '|') AS uq_scores_desc
				FROM yb_score
				GROUP BY subject;
?	窗口函数
	?语法:WINDOW_FUNC[expr] OVER(PARTITION BY | ORDER BY | ...)
		  先通过窗口规范对数据行进行分组,排序和计算,再进行聚合。

?	作用:针对窗口范围内数据的计算和聚合操作,实现横向扩展。

?	注意: 窗口函数一定需要在子查询中,即where子句需要在查询之外。
		  窗口函数不可聚合。
		  SELECT中存在非聚合列的情况下,使用窗口函数或者使用不影响计算结果的聚合函数聚合非聚合列。
?		  不可用DISTINCT。
?
?		序号函数:ROW_NUMBER()、RANK()(并列有影响)、DENSE_RANK()(并列无影响 ?)
				  排名还是用dense_rank(),不用rank()|limit。
?				  select FIELD_NAME,ROW_NUMBER() over(order by FIELD_NAME) as rk => FIELD_NAME(按照升序排序的值) ,rk(1,2,3...)
?		分布函数:PERCENT_RANK()、CUME_DIST()
?		前后函数:LAG(COLNAME,n,default)、LEAD(COLNAME,n,default)
?			第一个参数为列名,第二个参数为获取向上/下第n行,第三个参数为获取的数据行为Null时候取默认值。
?		头尾函数:FIRST_VALUE(expr)、LAST_VALUE(expr):开窗,窗口中显示expr经过排序后的首|尾值
?		其他函数:NTH_VALUE(expr,N)、NTILE(n)、
?				  SUM(FIELD_NAME):表示对当前行之前(包括当前行)的所有行对应字段的值求和,需要指定order by才能生效。
?								  如果不指定order by,默认为对该分组的所有值进行求和。
?				  SUM(CONDITION):统计符合条件的数量,注意如果为NULL,结果为0;(COUNT(COLUMN_NAME)则没有结果,因为COUNT自动过滤NULL)
?			 		CONDITION:CASE WHEN THEN...
				  [<ROWS or RANGE clause> BETWEEN <Start expr> AND <End expr>]:定义窗口范围(如有partition by,则在分区内定义范围)
				  Start expr | End expr :
					Current Row	当前行
					N preceding	前 n 行,n 为数字, 比如 2 Preceding 表示前2行
					unbounded preceding	开头
					N following	后N行,n 为数字, 比如 2 following 表示后2行
					unbounded following	结尾
					
					若不写即为rows between unbounded preceding and current row
					
					ROWS : 依据排序后的行号,取N行范围的数据。(可以通过构造ROW_NUMBER(ORDER BY ...))
						OVER(ORDER BY id RANGE BETWEEN 5 preceding AND current row)
					RANGE : 依据排序的列,获取排序列值范围的数据。
						OVER(ORDER BY salary RANGE BETWEEN 10000 preceding AND 10000 following)
					
		场景:求每个班、每个科目成绩的稳定程度。
			需求分析:稳定程度 -> 求欧氏距离(与平均值的差值) -> 将平均成绩扩展为窗口 avg(abs(每个学生的成绩-这个班这个科目的平均成绩)) 
					  需要[班级][科目][成绩],不需要[学生信息],可以直接跳过student_info表直接将stu_class_relation与其相连
			代码:select
					C.class_name,U.subject_name,I.score,
					avg(I.score) over(partition by C.class_name,U.subject_name) as avgByCU
				 from score_info I
				 inner join subject_info U
				 on U.subject_id = I.fk_subject_id
				 #inner join student_info S
				 on S.stu_id = I.fk_stu_id# => 跳过物理关系选取逻辑关系
				 inner join stu_class_relation R
				 on I.fk_stu_id = R.fk_stu_id
				 inner join class C
				 on R.fk_class_id = C.class_id
			此处使用窗口+聚合函数来实现,通过班级和科目进行分区,如果没有partition by,则默认作用于整个结果集
			partition by(a,b)表示每个a的每个b共享一个窗口
		
		场景:求每个班每个科目按成绩正序排每三个的平均成绩
			需求分析:为防止有数据缺项,长度为3的滑动窗口步长为1进行移动。=>如果所有数据参与计算,并且使用通用的计算方式,保证一定的数据量,结果就是有意义的。
					  不通过排名来定义编号(可能出现并列的情况),通过行号来定义编号。
		班级 科目 成绩 1
		班级 科目 成绩 2
		班级 科目 成绩 3
		班级 科目 成绩 4
		班级 科目 成绩 5
		班级 科目 成绩 6
		
		场景:求每个班级每个科目的升序成绩和下一行的差值。
			需求分析;
			
			代码:over(partition by C.class_name,U.subject_name) 
				  窗口粒度:每个班每个科目
				  over(partition by C.class_name,U.subject_name,order by score asc)
				  窗口粒度:从当前窗口的第一行至当前行(默认效果)
				  over(partition by C.class_name,U.subject_name,order by score asc unbounded preceding unbounded following)
				  窗口粒度:每个班级每个科目		
				  select
						C.class_name,U.subject_name,
						first_value(I.score) over win_cu as first_score,
						lag(I.score,1,-1) over win_cu as prev_score,
						I.score,
						lead(I.score,1,-1) over win_cu as next_score,
						last_value(I.score) over win_cu as last_score
				  from score_info I
				  inner join subject_info U
				  on U.subject_id = I.fk_subject_id
				  inner join stu_class_relation R
				  on I.fk_stu_id = R.fk_stu_id
				  inner join class_info C
				  on R.fk_class_id = C.class_id
				  window win_cu as(
					partition by C.class_name,U.subject_name
					order by I.score
					range between unbounded preceding and unbounded following
				  );
递归函数
	语法:
		with recursive cte_name (col1,col2) as (
			-- 初始查询
			SELECT column1, column2, ...
		    FROM table
		    WHERE condition
		    
			-- 递归查询
		    SELECT column1, column2, ...
		    FROM cte_name(递归实现的关键)
		    WHERE condition(指定递归的终止条件)
		)
SQL查询优化:
	列裁剪:仅选择所需字段
	行裁剪:on | where | 分组聚合[去重]
	去重:group by | distinct
	列扩展:窗口函数 | 子查询

SQL语句执行顺序——"fojwghsda聚ol"
	FROM 子句:检索数据表。
	ON子句:筛选出符合条件的行
	JOIN子句:表连接
	WHERE 子句:根据指定的条件筛选行。
	GROUP BY 子句:根据指定的列或表达式对行进行分组。/Partition by 子句
	HAVING 子句:根据条件筛选分组的行。
	SELECT 子句:选择将在结果集中返回的列或表达式。
	DISTINCT 子句:去重
	AS ALIAS 子句:别名
	聚合函数:计算汇总值,如 COUNT、SUM、AVG、MAX、MIN 等。
	ORDER BY 子句:排序。(ORDER BY 不能用于INNER JOIN(中的子查询))
	LIMIT/OFFSET 子句:限制结果集中返回的行数。


			  inner join subject_info U
			  on U.subject_id = I.fk_subject_id
			  inner join stu_class_relation R
			  on I.fk_stu_id = R.fk_stu_id
			  inner join class_info C
			  on R.fk_class_id = C.class_id
			  window win_cu as(
				partition by C.class_name,U.subject_name
				order by I.score
				range between unbounded preceding and unbounded following
			  );
			  
递归函数
	语法:
		with recursive cte_name (col1,col2) as (
			-- 初始查询
			SELECT column1, column2, ...
		    FROM table
		    WHERE condition
		    
			-- 递归查询
		    SELECT column1, column2, ...
		    FROM cte_name(递归实现的关键)
		    WHERE condition(指定递归的终止条件)
		)
SQL查询优化:
	列裁剪:仅选择所需字段
	行裁剪:on | where | 分组聚合[去重]
	去重:group by | distinct
	列扩展:窗口函数 | 子查询

SQL语句执行顺序——"fojwghsda聚ol"
	FROM 子句:检索数据表。
	ON子句:筛选出符合条件的行
	JOIN子句:表连接
	WHERE 子句:根据指定的条件筛选行。
	GROUP BY 子句:根据指定的列或表达式对行进行分组。/Partition by 子句
	HAVING 子句:根据条件筛选分组的行。
	SELECT 子句:选择将在结果集中返回的列或表达式。
	DISTINCT 子句:去重
	AS ALIAS 子句:别名
	聚合函数:计算汇总值,如 COUNT、SUM、AVG、MAX、MIN 等。
	ORDER BY 子句:排序。(ORDER BY 不能用于INNER JOIN(中的子查询))
	LIMIT/OFFSET 子句:限制结果集中返回的行数。

文章来源:https://blog.csdn.net/m0_74120525/article/details/135474505
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。