我半途而废的学习笔记之 MySQL (2)

这是第一篇文章.有数据链接

个人感觉查询这块知识点有些繁琐,反正我到后面就是在赶进度了,而且很多案例是一题多解的,用前面的知识点也完全OK,只是为了讲这个知识点强行安排,所以我取了很多小标题,方便翻查。

放个图方便大家做题:
在这里插入图片描述

一个有非常多小标题的目录

3.3. 进阶3 - 排序查询

            ************************
引入:
	select * from employees;
语法:
	SELECT 查询列表
	from 表
	[where 筛选条件]
	order by 排序列表 [asc|desc]
特点:
	1、asc代表升序,desc代表降序,默认不写是升序
	2、order by子句中可以支持单个字段、多个字段、表达式、函数、别名
	3、order by子句一般是放在查询语句的最后面,limit子句除外	
           **************************

案例1:查询员工信息,要求工资从高到低排序

#从高到低是降序吧
SELECT
	* 
FROM
	employees 
ORDER BY
	salary DESC;
#升序
SELECT
	* 
FROM
	employees 
ORDER BY
	salary ASC;

案例2:查询部门编号>=90的员工信息,按入职时间的先后进行排序

SELECT
	* 
FROM
	employees 
WHERE
	department_id >= 90 
ORDER BY
	hiredate ASC;

案例3:按年薪高低显示员工的信息和年薪[按表达式排序]

SELECT
	*,
	salary * 12 *(
	1+IFNULL ( commission_pct, 0 )) AS 年薪 
FROM
	employees 
ORDER BY
	salary * 12 *(
	1+IFNULL ( commission_pct, 0 )) DESC;

案例4:按年薪高低显示员工的信息和年薪[按别名排序]

SELECT
	*,
	salary * 12 *(
	1+IFNULL ( commission_pct, 0 )) AS 年薪 
FROM
	employees 
ORDER BY
	年薪 DESC;

案例5:按姓名的长度显示员工的姓名和工资[按函数排序]

SELECT
	LENGTH( last_name ) AS 字节长度,
	last_name,
	salary 
FROM
	employees 
ORDER BY
	LENGTH( last_name ) DESC;

案例6:查询员工信息,要求先按工资升序,再按员工编号降序 [按多个字段排序]

SELECT
	* 
FROM
	employees 
ORDER BY
	salary ASC,
	employee_id DESC;
#整体按工资排序,工资相同时,按员工编号降序

3.4. 进阶4 - 常见函数

            ************************
概念:
	将一组逻辑语句封装在方法体中,对外暴露方法名
好处:
	1、隐藏了实现细节
 	2、提高代码的重用性
调用:
	select 函数名(实参列表) [from 表(根据需要)];
特点:
	①叫什么(函数名)
	②干什么(功能)

分类:
	
	1、单行函数(字符函数,数学函数,日期函数,其他函数,流程控制函数)
	如concat、length、ifnull等
	
	2、分组函数
	功能:做统计使用,又称为统计函数、聚合函数、组函数

直接查阅:

字符函数:
	length:获取字节长度
	concat:连接
	substr:截取子串
	instr:获取字串第一次出现的索引
	trim:去掉前后空格 
	upper:变大写 
	lower:变小写
	lpad:左填充
	rpad:右填充 
	replace:替换
	
数学函数: 
	round:四舍五入
	ceil:向上取整
	floor:向下取整 
	truncate:截断
	mod:去模
	rand:获取随机数,返回0 - 1之间的小数
	
日期函数:
	now:当前日期+时间 
	curdate:返回当前日期
	curtime:返回当前时间
	monthname:以英文姓氏返回月
	year:年 
	month:月 
	day:日
	hour:小时 
	minute:分钟 
	second:秒
	datediff:两个日期之间的差的天数 
	str_to_date:将字符转换成日期  
	date_format:将日期转换成字符
	
其他函数:
	version:当前服务器的版本
	database:当前打开的数据库
	user:当前用户
	password('字符'):返回该字符的密码形式
	md5('字符'):返回该字符的md5加密格式
	
控制函数: 
	①if(条件表达式,表达式1,表达式2):如果条件表达式成立,则执行表达式1,不然则执行表达式2 
	②case:情况1
	case 变量或表达式或字段
	when 常量1 then 值1
	when 常量2 then 值2
	……
	else 值n
	end
	③case:情况2
	case 
	when 条件1 then 值1
	when 条件2 then 值2
	……
	else 值n
	end
		            ************************

####show variables like ‘%char%’
utf8编码的汉字一个汉字是3个字节,gbk编码的汉字一个汉字是2个字节,英文都是1个字节

3.4.1. 字符函数

length()

#获取参数值的字节个数
SELECT
	LENGTH( 'john' );
SELECT
	LENGTH( '张三丰hahaha' );	

concat()

#拼接字符串(用下划线拼接)
SELECT
	CONCAT( last_name, '_', first_name ) 
FROM
	employees;

upper()、 lower()

SELECT
	UPPER( 'john' );#变大写
SELECT
	LOWER( 'JOHN' );#变小写
#示例:将姓变大写,名变小写,拼接 
SELECT
	CONCAT(
		UPPER( last_name ),
	LOWER( first_name )) AS 姓名 
FROM
	employees;

		            ************************

substr()、 substring()

#注意索引从1开始,下语句输出:
							|out_put|
							|和李四|
#截取从 指定索引处 后面 所有字符
SELECT
	SUBSTR( '张三和李四', 3 ) out_put;
             1 2 3 4 5 
#截取从 指定索引处 指定字符长度的 字符
#下面语句输出:
							|out_put|
							|张三|
SELECT
	SUBSTR( '张三和李四', 1, 2 ) out_put;	

#案例:姓名中首字符大写,其他字符小写,用_拼接,显示出来
SELECT
	CONCAT(
		UPPER(
		SUBSTR( last_name, 1, 1 )),
		'_',
		LOWER(
		SUBSTR( last_name, 2 ))) output 
FROM
	employees;	

		            ************************

instr()

#返回 子串的 起始索引,找不到返回0
下例返回7
SELECT
	INSTR( '杨不悔爱上了殷六侠', '殷六侠' ) AS out_put	
			1 2 3 4 5 67
		            ************************
#6、trim 去掉首尾(空格不一定)
#输出 张翠山
SELECT
	LENGTH(
	TRIM( '    张翠山      ' )) AS out_put;
#输出 张aaaa翠山
SELECT
	TRIM( 'a' FROM 'aaaa张aaaa翠山aaaaaaaa' ) AS out_put;	
		            ************************

lpad()

用 指定的字符 实现 左填充 指定长度,不够才填, 够了就保留
#输出*******殷素素
SELECT
	LPAD( '殷素素', 10, '*' ) AS out_put;
#输出:殷素
SELECT
	LPAD( '殷素素', 2, '*' ) AS out_put;

		            ************************

rpad()

用指定的字符实现右填充指定长度
#输出:殷素素ababababa
SELECT
	RPAD( '殷素素', 12, 'ab' ) AS out_put;

		            ************************
#9、replace 替换
SELECT REPLACE
	( '张无忌爱上了周芷若', '周芷若', '赵敏' ) AS out_put;

3.4.2. 字符函数

round()

四舍五入
SELECT
	ROUND( 1.65 );#2
SELECT
	ROUND(- 1.45 );#-1
SELECT
	ROUND( 1.567, 2 );#1.57,小数点后保留2位

ceil()

向上取整(返回>=该参数的最小整数)
SELECT
	CEIL(- 1.02 );#-1
SELECT
	CEIL( 1.00 );#1
SELECT
	CEIL( 1.02 );#2

floor()

向下取整,返回<=该参数的最大整数
SELECT
	FLOOR(- 9.99 );#-10

truncate()

截断
SELECT TRUNCATE
	( 1.65, 1 );#1.6;保留一位小数的意思

mod()

取余
#计算原理:
mod(a,b) : a - a/b * b
mod(-10,-3) : -10-(-10)/(-3)*(-3)=-1;
#举例:
SELECT MOD
	( 10,- 3 );#1

3.4.3. 日期函数

now()

返回当前系统日期加时间
SELECT
	NOW();

curdate()

返回当前系统日期,不包含时间
SELECT
	CURDATE();	

#curtime 返回当前时间,不包含日期
SELECT
	CURTIME();
	
#可以获取指定的部分,年,月,日,小时,分,秒
SELECT YEAR
	(
	NOW()) AS 年;#2020
SELECT YEAR
	( '1998-1-1' ) 年;#1998
SELECT YEAR
	( hiredate ) 年 
FROM
	employees;#hiredate是表里的字段-入职时间
SELECT MONTH
	(
	NOW()) 月;
SELECT
	MONTHNAME(
	NOW()) 月;#显示英文月份	

		            ************************

str_to_date()

将字符通过指定的格式转换成日期

%Y 四位的年份
%y 2位的年份
%m 月份 (01,02,...12)
%c 月份 (1,2,..., 12)
%d 日 
%H 小时(24)%h(12)
%i 分钟  %s秒



SELECT
	STR_TO_DATE( '9-13-1999', '%m-%d-%Y' ) 日期;#1999-09-13
					字符串        日期格式
SELECT 
	STR_TO_DATE ('2020-4-17','%Y-%c-%d') AS output;#2020-4-17
					字符串 		 日期格式
#查询入职日期为1992-4-3的员工信息
SELECT
	* 
FROM
	employees 
WHERE
	hiredate = '1992-4-3';
#
SELECT
	* 
FROM
	employees 
WHERE
	hiredate = STR_TO_DATE( '4-3 1992', '%c-%d %Y' );	
第二块告诉电脑前面这块谁是日谁是月谁是年
		            ************************

date_format()

将日期转换成字符
SELECT
	DATE_FORMAT( NOW(), '%y年%m月%d日' ) AS output;#20年4月17日	

#查询有奖金的员工名和入职日期(xx月/xx日 xx年)
SELECT
	last_name,
	DATE_FORMAT( hiredate, '%m月/%d日 %Y年' ) 入职日期 
FROM
	employees 
WHERE
	commission_pct IS NOT NULL;	

3.4.4. 其他函数

#使用较少
SELECT 
	VERSION();
SELECT 
	DATABASE();#查看当前库
SELECT 
	USER();

3.4.5. 流程控制函数

1. if函数:

if else 的效果

SELECT IF(10>5,'大','小');

SELECT
	last_name,
	commission_pct,
IF
	( commission_pct IS NULL, '呵呵,没奖金', '嘻嘻,有奖金' ) 备注 
FROM
	employees;

					************************

2. case函数的使用一:

switch case的效果

case 要判断的字段或表达式
when 常量1 then要显示的值1或语句1;
when 常量2 then要显示的值2或语句2;
...
else 要显示的值n或语句n
end
(搭配select当表达式,then加一个值,或者不搭配select当语句)


		            ************************
案例1:查询员工的工资:要求
部门号=30,显示的工资为1.1倍
部门号=40,显示的工资为1.2倍
部门号=50,显示的工资为1.3倍
其他部门显示原工资

代码:
SELECT
	salary,
	department_id,
CASE
		department_id 
		WHEN 30 THEN
		salary * 1.1 
		WHEN 40 THEN
		salary * 1.2 
		WHEN 50 THEN
		salary * 1.3 ELSE salary 
	END AS 新工资 
FROM
	employees;


		            ************************

3. case函数的使用二:

#类似于多重if

case  
when 条件1 then 要显示的值1或语句1
when 条件2 then 要显示的值2或语句2
...
else 要显示的值n或语句n
end
与上一种情况的不同就是case后不加表达式

		            ************************
案例1:查询员工的工资情况
如果工资>20000,显示A级别
如果工资>15000,显示B级别
如果工资>10000,显示C级别
否则显示D级别

代码:
SELECT
	salary,
CASE
		
		WHEN salary > 20000 THEN
		'A' 
		WHEN salary > 15000 THEN
		'B' 
		WHEN salary > 10000 THEN
		'C' ELSE 'D' 
	END AS 工资级别 
FROM
	employees;

3.4.6. 分组函数

功能:用作统计使用,又称为聚合函数或统计函数或组函数
分类:
sum、  avg、  max、  min、  count


特点
1、sum,avg用于处理数值型
max,min,count可以处于任何类型

2、以上分组函数都忽略null值

3、可以和distinct搭配实现去重

4、count函数的单独介绍,一般用count(*)多,统计行数

5、和分组函数一同查询的字段要求是group by后的字段

		            ************************

1. 简单的使用

SELECT
	SUM( salary ) 
FROM
	employees;
SELECT
	SUM( salary ) #和,ROUND(AVG(salary),2) #平均 FROM employees;
SELECT
	MAX( salary ) #最高, MIN(salary) #最低 FROM employees;
SELECT
	MIN( salary ) 
FROM
	employees;
SELECT
	COUNT( salary ) 
FROM
	employees;	
		            ************************

2. 参数支持哪些类型

SELECT
	SUM( last_name ),
	AVG( last_name ) 
FROM
	employees;
SELECT
	SUM( hiredate ),
	AVG( hiredate ) 
FROM
	employees;
#字符求和无意义,不建议这样用
#所以有的适合数值型数据,有的都适合。

SELECT
	MAX( last_name ),
	MIN( last_name ) 
FROM
	employees;
SELECT
	MAX( hiredate ),
	MIN( hiredate ) 
FROM
	employees;

#支持
SELECT
	COUNT( last_name ) 
FROM
	employees;#计算非空的值 107
SELECT
	COUNT( commission_pct ) 
FROM
	employees;#35

		            ************************	 

3. 是否忽略null?

#是的,忽略
SELECT
	SUM( commission_pct ),
	AVG( commission_pct ) 
FROM
	employees;
		            ************************  

4. 搭配distinct实现去重

SELECT
	SUM( DISTINCT salary ),
	SUM( salary ) 
FROM
	employees;
SELECT
	COUNT( DISTINCT salary ),
	COUNT( salary ) 
FROM
	employees;

5. count函数的详细介绍

SELECT
	COUNT( salary ) 
FROM
	employees;
SELECT
	COUNT(*) 
FROM
	employees;#统计每一列的数目,即所有行数,有一行里的一个不为NULL,就算上这一行
SELECT
	COUNT( 1 ) 
FROM
	employees;#和上一语句效果一样,加了一列全是1的列,里面可以任意给数字,给字符要加引号。	  

6. 和分组函数一同查询的字段有限制

SELECT 
	AVG(salary),
	employee_id 
FROM employees;
#这个员工id查出来没有意义  

3.5. 进阶五 - 分组查询

语法:
	select 分组函数(max,min这些),列(**要求出现在group by后面**)
	from 表
	[where 筛选条件]
	group by 分组的列表
	[order by]子句
注意:查询列表必须特殊,要求是分组函数和group by后出现的字段

特点:
	1、分组查询中的筛选条件分为两类(筛选的数据源不同
                        数据源           位置		    关键字
	分组前筛选:    原始表	   group by子句前面	    where
	分组后筛选:分组后的结果集   group by子句后面	    having
	
	①分组函数做条件,肯定是放在having子句中
	②能用分组前筛选的,优先考虑用分组前筛选
	
	2、group by子句支持单个字段分组,多个字段分组,
	(多个字段之间用逗号隔开无顺序要求),表达式或函数(用的较少)
	
	3、可添加排序(放在group by后)
		            ************************  	

3.5.1. 简单的分组查询

案例1:查询每个工种的最高工资
SELECT 
	MAX(salary),
	job_id
FROM 
	employees
GROUP BY 
	job_id;
案例2:查询每个位置上的部门个数
SELECT 
	COUNT(*),
	location_id
FROM 
	departments
GROUP BY 
	location_id;
		            ************************  	

3.5.2. 添加分组前筛选条件

案例1:查询邮箱中包含a字符的,每个部门的平均工资
SELECT
	AVG( salary ),
	department_id 
FROM
	employees 
WHERE
	email LIKE '%a%' #邮箱中包含a字符
	
GROUP BY
	department_id;
案例2:查询有奖金的每个领导手下员工的最高工资
SELECT
	MAX( salary ),
	manager_id 
FROM
	employees 
WHERE
	commission_pct IS NOT NULL 
GROUP BY
	manager_id;	


		            ************************  

3.5.3. 添加分组后的筛选条件

案例1:查询哪个部门的员工个数大于2
#①查询每个部门的员工个数
SELECT
	COUNT(*),
	department_id 
FROM
	employees 
GROUP BY
	department_id;
#②根据1的结果进行筛选
SELECT
	COUNT(*),
	department_id 
FROM
	employees 
GROUP BY
	department_id 
HAVING
	COUNT(*)> 2;#######重要##########
案例2:查询每个工种有奖金的员工的最高工资>12000的工种编号和其最高工资
(#有点难)
#①查询每个工种有奖金的员工的最高工资
SELECT
	MAX( salary ),
	job_id 
FROM
	employees 
WHERE
	commission_pct IS NOT NULL 
GROUP BY
	job_id;
#②根据1的结果继续筛选,最高工资>12000
SELECT
	MAX( salary ),
	job_id 
FROM
	employees 
WHERE
	commission_pct IS NOT NULL 
GROUP BY
	job_id 
HAVING
	MAX( salary )> 12000;
案例3:查询领导编号>102的每个领导手下的最低工资>5000的领导编号
#①查询领导编号>102的每个领导手下的最低工资
SELECT
	MIN( salary ),
	manager_id 
FROM
	employees 
WHERE
	manager_id > 102 
GROUP BY
	manager_id;	

#②在1的基础上,最低工资>5000
SELECT
	MIN( salary ),
	manager_id 
FROM
	employees 
WHERE
	manager_id > 102 
GROUP BY
	manager_id 
HAVING
	MIN( salary )> 5000;
		            ************************  

3.5.4. 按表达式或函数分组

#案例:按员工姓名的长度分组,查询每一组的员工个数,筛选员工个数>5的
SELECT
	COUNT(*),
	LENGTH( last_name ) len_name 
FROM
	employees 
GROUP BY
	len_name 
HAVING
	COUNT(*)> 5;

3.5.5. 按多个字段分组

#案例:查询每个部门每个工种的员工的平均工资
SELECT
	AVG( salary ),
	department_id,
	job_id 
FROM
	employees 
GROUP BY
	department_id,
	job_id;
		            ************************  

3.5.6. 按多个字段分组–添加排序

##案例:查询每个部门每个工种的员工的平均工资,并将>10000的按高低排序
SELECT
	AVG( salary ) a,
	department_id,
	job_id 
FROM
	employees 
GROUP BY
	department_id,
	job_id 
HAVING
	a > 10000 
ORDER BY
	a DESC;

3.6. 进阶六 - 连接查询

含义:
	又称多表查询,当查询的字段来自多个表,就会用到连接查询

笛卡尔乘积现象:表1有m行,表2有n行,结果:m*n行
 
发生原因:没有有效的连接条件
如何避免:添加有效的连接条件

分类:
按年代分类:
	sql92标准:仅仅支持内连接
	sql99标准【推荐】:支持内连接 + 外连接(左外,右外)+ 交叉连接
	
按功能分类:
	内连接:
			等值连接,
			非等值连接,
			自连接
	外连接:
			左外连接,
			右外连接,
			全外连接
	交叉连接

3.6.1. sql92标准

1. 等值连接

①多表等值连接的结果为多表的交集部分
②n表连接,至少需要n-1个连接条件
③多表的顺序无要求
④一般需要为表起别名
⑤可以搭配前面介绍的所有查询子句使用,比如排序,分组,筛选
案例1:查询女神名和对应的男神名
SELECT NAME
	,
	boyName 
FROM
	boys,
	beauty 
WHERE
	beauty.boyfriend_id = boys.id;
案例2:查询员工名和对应的部门名
SELECT
	last_name,
	department_name 
FROM
	employees,
	departments 
WHERE
	employees.department_id = departments.department_id;

		            ************************

#2、为表起别名

①提高语句简洁度
②区分多个重名的字段
③如果为表起了 别名,则 查询的字段 就不能用 原来的表名 去限定,只能用 别名
案例3:查询员工名、工种号、工种名
SELECT
	last_name,
	e.job_id,
	job_title 
FROM
	employees AS e,
	jobs AS j #执行顺序是这句先
	
WHERE
	e.job_id = j.job_id;
#两个表的顺序可以调换
SELECT
	last_name,
	e.job_id,
	j.job_title 
FROM
	jobs AS j,
	employees AS e 
WHERE
	e.job_id = j.job_id;

					************************ 
#3、可以加筛选
案例4:查询有奖金的员工名、部门名
SELECT
	last_name,
	department_name,
	commission_pct 
FROM
	employees e,
	departments d 
WHERE
	e.department_id = d.department_id 
	AND e.commission_pct IS NOT NULL;
案例5:查询出城市名中第二个字符为o的部门名和城市名
SELECT
	department_name,
	city 
FROM
	departments d,
	locations l 
WHERE
	d.location_id = l.location_id 
	AND city LIKE '_o%';

		            ************************
		            
#4、加分组
案例6:查询每个城市的部门个数
SELECT
	COUNT(*) 个数,
	city 
FROM
	departments d,
	locations l 
WHERE
	d.location_id = l.location_id 
GROUP BY
	city;	
案例7:查询有奖金的每个部门的部门名和部门的领导编号,和该部门的最低工资
SELECT
	department_name,
	d.manager_id,
	MIN( salary ) 
FROM
	departments d,
	employees e 
WHERE
	d.department_id = e.department_id
	AND commission_pct IS NOT NULL 
GROUP BY
	department_name;

		            ************************
#5、加排序
案例8:查询每个工种的工种名和员工个数,按员工个数降序
SELECT
	job_title,
	COUNT(*) 
FROM
	employees e,
	jobs j 
WHERE
	e.job_id = j.job_id
GROUP BY
	job_title 
ORDER BY
	COUNT(*) DESC;	

		            ************************
#6、实现三表连接
案例9:查询员工名、部门名和所在的城市
SELECT
	last_name,
	department_name,
	city 
FROM
	employees e,
	departments d,
	locations l 
WHERE
	e.department_id = d.department_id
	AND d.location_id = l.location_id;

#####################################################

2. 非等值连接

#创建工资级别
#运行下面这段代码,在数据库中创建一个job_grades表
CREATE TABLE job_grades
(grade_level VARCHAR(3),
lowest_sal INT,
highest_sal INT);

INSERT INTO job_grades
VALUES ('A',1000,2999);

INSERT INTO job_grades
VALUES ('B',3000,5999);

INSERT INTO job_grades
VALUES ('C',6000,9999);

INSERT INTO job_grades
VALUES ('D',10000,14999);

INSERT INTO job_grades
VALUES ('E',15000,24999);

INSERT INTO job_grades
VALUES ('F',25000,40000);
		            ************************

案例:查询员工的工资和工资级别
SELECT
	salary,
	grade_level 
FROM
	employees e,
	job_grades g 
WHERE
	salary BETWEEN g.`lowest_sal` #笔记1有说这里为什么要加引号? 
	AND g.`highest_sal`;

3. 自连接

#有点像等值连接
#案例:查询员工名和上级的名称
SELECT
	e.employee_id,
	e.last_name,
	m.employee_id,
	m.last_name 
FROM
	employees e,   #一张表看成两个表
	employees m 
WHERE
	e.manager_id = m.employee_id;

3.6.2. sql99标准

语法:
	select 查询列表
	from 表1 别名 【连接类型】
	join 表2 别名 on 【连接条件】
	on 连接条件
	【where 筛选条件】
	【group by 分组】
	【having 筛选条件】
	【order by 排序列表】

分类:
内连接:inner
外连接:

	左外 left  【outer】
	右外 right 【outer】
	全外 full  【outer】
交叉连接:cross

		            ************************

内连接

语法:
select 查询列表
from 表1 别名
inner join 表2 别名
on 连接条件;

分类:
等值
非等值
自连接

特点:
①添加排序、分组、筛选
②inner可以省略
③筛选条件放在where后面,连接条件放在on后面,提高分离性,便于阅读
④inner join连接和sql92语法中的等值连接效果一样,都是查询多表的交集

		            ************************

1. 等值连接

#案例1 查询员工名,部门名
SELECT
	last_name,
	department_name 
FROM
	employees e
	INNER JOIN departments d ON e.department_id = d.department_id;



#案例2:查询名字中包含e的员工名和工种名(添加筛选)
SELECT
	last_name,
	job_title 
FROM
	employees e
	INNER JOIN jobs j ON e.job_id = j.job_id 
WHERE
	e.last_name LIKE '%e%';

#案例3:查询部门个数>3的城市名和部门个数(添加分组+筛选)
SELECT
	city,
	COUNT(*) 部门个数 
FROM
	locations l
	INNER JOIN departments d ON d.location_id = l.location_id 
GROUP BY
	city 
HAVING
	COUNT(*)> 3;

#案例4:查询哪个部门的部门员工个数>3的部门名,和员工个数,并按个数进行降序
SELECT
	department_name,
	COUNT(*) 
FROM
	employees e
	INNER JOIN departments d ON e.department_id = d.department_id 
GROUP BY
	department_name 
HAVING
	COUNT(*)> 3 
ORDER BY
	COUNT(*) DESC;

#案例5:查询员工名、部门名、工种名,并按部门名降序(三表连接)
SELECT
	last_name,
	department_name,
	job_title 
FROM
	employees e
	INNER JOIN departments d ON e.department_id = d.department_id
	INNER JOIN jobs j ON e.job_id = j.job_id 
ORDER BY
	d.department_name DESC;

		            ************************

2. 非等值连接

#案例1:查询员工的工资级别
SELECT
	salary,
	grade_level 
FROM
	employees e
	INNER JOIN job_grades g ON e.salary BETWEEN g.lowest_sal 
	AND g.highest_sal;

#案例2:查询工资的个数>20的级别个数,并且按工资级别降序

SELECT
	salary,
	grade_level,
	COUNT(*) 
FROM
	employees e
	INNER JOIN job_grades g ON e.salary BETWEEN g.lowest_sal 
	AND g.highest_sal 
GROUP BY
	grade_level 
HAVING
	COUNT(*) > 20 
ORDER BY
	grade_level DESC;

		            ************************

3. 自连接

#案例1:查询员工的名字,上级的名字
SELECT
	e.last_name,
	m.last_name 
FROM
	employees e
	JOIN employees m ON e.manager_id = m.employee_id;
#加筛选:姓名中包含字符k的员工名字、上级名字
SELECT
	e.last_name,
	m.last_name 
FROM
	employees e
	JOIN employees m ON e.manager_id = m.employee_id 
WHERE
	e.last_name LIKE '%k%';


		            ************************

外连接

#有主从表,主表就是你要查的信息主要来自的表
应用场景:
	用于查找一个表中有,另一个表中没有的记录
特点:
1、外连接的 查询结果 为主表中 所有的记录
   如果 从表中 有和它 匹配的,则显示 匹配的值
   如果 从表中 没有和它 匹配的,则显示 null
   外连接查询结果 = 内连接结果 + 主表中有 而从表中没有的 记录
2、左外连接:left jon左边的是主表
   右外连接:right join右边的是主表

3、左外和右外交换两个表的顺序,可以实现同样的效果
4、全外连接(不支持) = 内连接结果 + 表1有 但表2没有 + 表2有 但表1没有

案例1:查询男朋友不在男生表的女生名
(结果里没有的都是null)
#左外连接
SELECT
	be.NAME,
	bo.* 
FROM
	beauty be  #主表
	LEFT OUTER JOIN boys bo ON be.boyfriend_id = bo.id;

#右外连接
SELECT
	be.NAME,
	bo.* 
FROM
	boys bo    #从表
	RIGHT OUTER JOIN beauty be ON be.boyfriend_id = bo.id 
WHERE
	bo.id IS NOT NULL;
案例2:查询哪个部门没有员工
#左外
谁是主表?部门表吧?
SELECT
	d.*,
	e.employee_id 
FROM
	departments d
	LEFT OUTER JOIN employees e ON d.department_id = e.department_id 
WHERE
	e.employee_id IS NULL;

#右外
SELECT
	d.*,
	e.employee_id 
FROM
	employees e
	RIGHT OUTER JOIN departments d ON d.department_id = e.department_id 
WHERE
	e.employee_id IS NULL;

		            ************************

交叉连接(笛卡尔乘积)

SELECT
	be.*,
	bo.* 
FROM
	beauty be
	CROSS JOIN boys bo;
##建议使用sql99语法

看两个图片意会一下:
在这里插入图片描述
在这里插入图片描述
最好是在写之前,能过一眼数据,感受一下大概是要取什么,取出来大概是什么样子。

3.7 进阶七 - 子查询

#可能是最难的查询

含义:出现在 其他语句内部的 select语句,称为 子查询 或 内查询
外部的 查询语句,称为 主查询 或 外查询

分类:
1. 按子查询出现的位置:
	     select后面:        仅仅支持 标量子查询
	     from后面:          支持 表子查询
      ☆ where或者having后面:标量子查询,列子查询,行子查询
	     exists后面(相关子查询):表子查询
2. 按结果集的行列数不同:
	    标量子查询(结果集只有一行一列)
		列子查询(结果集只有一列多行)
		行子查询(结果集有一行多列)
		表子查询(结果集一般为多行多列)


		            ************************

3.7.1. where或having后面

#1、标量子查询
#2、列子查询(多行子查询)
#3、行子查询(多行多列)

特点:
①子查询放在小括号内
②子查询一般放在条件右侧
③标量子查询,一般搭配单行操作符使用
> < >= <= = <>

列子查询一般搭配多行操作符使用
IN/NOT IN、ANY/SOME、ALL
任意一个    某一个    所有		
④子查询的执行优先于主查询执行


		            ************************

标量子查询

#案例1:谁的工资比Abel高

#①查询abel的工资
SELECT salary 
FROM employees
WHERE last_name='Abel';
#②查询员工的信息,满足salary>①的结果
SELECT *
FROM employees
WHERE salary>(
	SELECT salary 
	FROM employees
	WHERE last_name='Abel'
);

#案例2:返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资

#①查询141号员工的job_id
SELECT 
	job_id
FROM 
	employees
WHERE 
	employee_id=141;

#②查询143号员工的salary
SELECT 
	salary
FROM 
	employees
WHERE 
	employee_id=143;

#③员工的姓名,job_id,工资,要求job_id=①并且salary>②
SELECT
	last_name,
	job_id,
	salary 
FROM
	employees 
WHERE
	job_id =(
	SELECT
		job_id 
	FROM
		employees 
	WHERE
		employee_id = 141 
	) 
	AND salary >(
	SELECT
		salary 
	FROM
		employees 
	WHERE
	employee_id = 143 
	);

#案例3:返回公司工资最少的员工的last_name,job_id,salary
#①查询公司的最低工资
SELECT 
	MIN(salary) 
FROM 
	employees

#②查询last_name,job_id
SELECT
	last_name,
	job_id,
	salary 
FROM
	employees 
WHERE
	salary = ( SELECT MIN( salary ) FROM employees );

#案例4:查询最低工资大于50号部门的最低工资的部门id和其最低工资
#①查询50号部门的最低工资
SELECT
	MIN( salary ) 
FROM
	employees 
WHERE
	department_id = 50;

#②查询每个部门的最低工资
SELECT
	MIN( salary ) 
FROM
	employees 
GROUP BY
	department_id;

#③筛选②
SELECT
	MIN( salary ),
	department_id 
FROM
	employees 
GROUP BY
	department_id 
HAVING
	MIN( salary ) > ( SELECT MIN( salary ) FROM employees WHERE department_id = 50 );
	



		            ************************

列子查询(多行子查询)

#案例1:返回location_id是1400或1700的部门中所有员工姓名
**法一:**
#①查询location_id是1400或1700的部门编号
SELECT DISTINCT 
	department_id
FROM 
	departments
WHERE 	
	location_id IN(1400,1700);

#②查询员工姓名要求部门号是①中的某个
SELECT
	last_name 
FROM
	employees 
WHERE
	department_id IN ( SELECT DISTINCT department_id FROM departments WHERE location_id IN ( 1400, 1700 ) );#in可以换成 =ANY
#我真的搞不懂navicat这个美化为什么这里是一整行,不如老师的sqlyog好看呢
**法二:**
SELECT
	last_name 
FROM
	employees e
	LEFT JOIN departments d ON e.department_id = d.department_id 
WHERE
	d.location_id IN ( 1400, 1700 );
#in与(= ANY)、not in与 (<>ALL)
#为什么不用连接查询?因为子查询效率更高


		            ************************
#案例2:返回其他工种中比job_id为‘IT_PROG'的任一工资低的员工信息
#这题的题意不太好理解哈
#用any 
SELECT
	employee_id,
	last_name,
	job_id,
	salary 
FROM
	employees 
WHERE
	salary < ANY ( SELECT DISTINCT salary FROM employees WHERE job_id = 'IT_PROG' ) 
	AND job_id <> 'IT_PROG';
	



#案例3:返回其他工种中比job_id为‘IT_PROG'的所有工资低的员工信息
SELECT
	employee_id,
	last_name,
	job_id,
	salary 
FROM
	employees 
WHERE
	salary < ALL ( SELECT DISTINCT salary FROM employees WHERE job_id = 'IT_PROG' ) 
	AND job_id <> 'IT_PROG';


		            ************************

行子查询(结果集是一行多列或多行多列)

#用的较少
#案例:查询员工编号最小并且工资最高的员工信息
SELECT
	* 
FROM
	employees 
WHERE
	employee_id = ( SELECT MIN( employee_id ) FROM employees ) 
	AND salary = ( SELECT MAX( salary ) FROM employees );
#或者
SELECT
	* 
FROM
	employees 
WHERE
	( employee_id, salary ) = ( SELECT MIN( employee_id ), MAX( salary ) FROM employees );

3.7.2. select后面

仅支持标量子查询 一行一列的

#案例1:查询每个部门的员工个数
#我第一时间会用连接 + 分组 做
SELECT
	d.*,
	( SELECT COUNT(*) FROM employees e WHERE e.department_id = d.department_id ) 个数 
FROM
	departments d;
#子查询真的是..又回到被SAS支配中了


#案例2:查询员工号=102的部门名
SELECT
	( SELECT department_name FROM departments d INNER JOIN employees e ON d.department_id = e.department_id WHERE e.employee_id = 102 );
#这题的答案真是强行嵌套..穿着雨衣打伞啊,我傻了
#往后我不去理解了,明明可以内联,我都快傻了,跳过!
#这美化的是美化了个锤子,又是一行

3.7.3. from后面

#案例:查询每个部门的平均工资的工资等级

#①查询每个部门的平均工资
SELECT 
	AVG(salary),
	department_id
FROM 
	employees
GROUP BY 
	department_id;

#②连接①的结果集和等级表,筛选条件 平均工资between最低和最高
SELECT
	ag_dep.*,
	g.grade_level 
FROM
	( SELECT AVG( salary ) ag, department_id FROM employees GROUP BY department_id ) ag_dep
	INNER JOIN job_grades g ON ag_dep.ag BETWEEN g.lowest_sal 
	AND g.highest_sal;

3.7.4. exists后面

exists完整的查询语句
结果:1或0

SELECT EXISTS
	(
	SELECT
		employee_id 
FROM
	employees)#结果为1,表示存在

#案例1:查询有员工的部门名
SELECT
	department_name 
FROM
	departments d 
WHERE
	d.`department_id` IN ( SELECT department_id FROM employees );
#或
SELECT
	department_name 
FROM
	departments d 
WHERE
	EXISTS ( SELECT * FROM employees e WHERE e.department_id = d.department_id );	

#案例2:查询没有女朋友的男生信息
#in
SELECT
	bo.* 
FROM
	boys bo 
WHERE
	bo.id NOT IN ( SELECT boyfriend_id FROM beauty );#exists
SELECT
	bo.* 
FROM
	boys bo 
WHERE
	NOT EXISTS ( SELECT boyfriend_id FROM beauty be WHERE bo.id = be.boyfriend_id );

3.8. 进阶八 - 分页查询

应用场景:当要显示的数据一页显示不全,需要分页提交sql请求
语法:
		select 查询列表
		from 表
		{join type join 表2
		where 筛选条件
		group by 分组字段
		having 分组后的筛选
		order by 排序的字段}
		limit offset,size;
			   参数	  参数
		offset:要显示条目的起始索引,起始索引从0开始
		size:要显示的条目数
特点:
		①limit语句放在查询语句的**最后**
		②公式
		要显示的页数page,每页的条目数size
		
		select 查询列表
		from 表
		limit (page-1)*size,size;
		size = 10
		page
		1		0
		2		10
		3		20

案例1:查询前五条员工信息

SELECT
	* 
FROM
	employees 
	LIMIT 0,
	5;
SELECT
	* 
FROM
	employees 
	LIMIT 5;
#	

案例2:查询第11条 到 第25条(一共25-11+1条)

SELECT
	* 
FROM
	employees 
	LIMIT 10,
	15;#索引从0开始 #索引从0开始

案例3:有奖金的 员工信息,并且工资较高的 前10名 显示

SELECT
	* 
FROM
	employees 
WHERE
	commission_pct IS NOT NULL 
ORDER BY
	salary DESC 
	LIMIT 10;

3.9. 进阶九 - 联合查询

union 联合 合并
语法:
查询语句1
union
查询语句2
union 
……

应用场景:
要查询的结果来自于多个表,且多个表没有直接的连接关系,但查询的信息一致时

特点:
1. 要求多条查询语句的查询列数是一致的
2. 要求多条查询语句的查询的每一列的类型和顺序最好一致
3. 用union会自动去重,不想去重的话就用union all

案例1 查询部门编号>90或邮箱包含a的员工信息

SELECT
	* 
FROM
	employees 
WHERE
	email LIKE '%a%' 
	OR department_id > 90;
#用union
SELECT
	* 
FROM
	employees 
WHERE
	email LIKE '%a%' UNION
SELECT
	* 
FROM
	employees 
WHERE
	department_id > 90;
#上面这个例子看不出来union的意义哈,我看着还怪麻烦

案例2 查询中国用户中男性的信息以及外国用户中年男性的用户信息

#我是sql8.0没test这个库,不过看看代码也能明白他想干啥
SELECT
	id,
	cname,
	csex 
FROM
	t_ca 
WHERE
	csex = "男" UNION
SELECT
	t_id,
	tName,
	tGender 
FROM
	t_ua 
WHERE
	tGender = 'male';
#不好意思我懂了,union的好处是当你一个库里 好几个表之间 没有那种可以建立连接的关系,用这个就太方便了
#上面这个例子啊,两个表的字段不一样,但是意义却一样,如果内容不一样的话,合不起来

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_41661059/article/details/106892720