DQL语言的学习
一、DQL语言的学习
进阶1:基础查询
语法select 查询列表 from 表名;
一般字段名加上着重号,也可以不加
特点:
- 查询列表可以是:表中的字段、常量值、表达式、函数
- 查询的结果是一个虚拟的表格
举例:
1.查询表中单个字段
selectlast_name
from employees;2.查询表中多个字段
selectlast_name
,salary from employees;3.查询表中所有字段
select * from employees;4.查询常量值
select 100;
select ‘jack’5.查询表达式
select 100%3;6.查询函数
select version();//查版本号7.起别名
便于理解
如果要查询的字段有重名的情况,使用别名可以区分开来
方式一:使用as
select 100 %3 as 结果;
select last_name as 姓, first_name as 名 from employees;
方式二:使用空格
select last_name 姓, first_name 名 from employees;
注意:如果别名中有特殊符号:空格、’#'等,则要给别名加上双引号或单引号8.去重 distinct
select distinct department_id from employees;9.+号的作用 只有一个作用:运算符
两个操作数都为数值型,则做加法运算
一方为字符型,则试图将字符型转换成数值型
如果转换成功,则继续做加法运算
如果转换失败,则将字符型数值转换成0
只要其中一方为null,则结果肯定为null10.使用concat实现连接字符
注意:只要其中一方为null,则结果肯定为null
select concat(‘a’,‘b’,‘c’) as 结果;//如果是数值型也会自动转为字符
select concat(last_name,first_name) as 姓名 from employees;//把姓氏名称拼接成姓名11.ifnull(a,b) 判断某字段或表达式(a)是否为null,如果为null返回指定的值(b),否则返回原来的值
select ifnull(commission_pct,0) as 奖金率, commission_pct from employees;
12.isnull(a)函数,判断某字段或表达式(a)是否为null,如果是返回1,否则返回0
进阶2:条件查询
语法: select 查询列表③ from 表名① where 筛选条件②;
根据筛选条件分类:
- 按条件表达式筛选
- 条件运算符:> < = !=/<> >= <=
- 按逻辑表达式筛选
- 逻辑运算符:and、or、not
- 模糊查询
- like、between and、in、is null
- like一般和通配符搭配使用,可以判断字符型或数值型
- %:任意多个组放,包含0个字符
- _:任意单个字符
- \ 转义字符
- between and:包含临界值,相当于 1<= x<=2
- in:判断某字段的值是否属于in列表中的某一项
- 特点:使用in提高语句简洁度
- in列表的值类型必须一致或兼容
- is null:
- = 或<> 不能用于判断null值
- is null 或is not null 仅可以判断null值,可读性较高,建议使用
- 安全等于:<=> 不仅可以判断null值还可以判断普通类型的值,可读性较低
举例:
一、按条件表达式筛选
select * from employees where salary > 12000;二、按逻辑表达式筛选
select last_name, salary, commission_pct from employees where salary>=10000 and salary<=20000;三、模糊查询
1.like
select * from employees where last_name like ‘%a%’;//查询员工名中包含字符a的员工信息
select last_name, salary from employees where last_name like ‘__e_a%’;//员工名中第三个字符为e,第五个字符为a
select last_name, salary from employees where last_name like ‘__%’;//员工名第二个字为_的员工名,要用到转义字符 \2.between and
select * from employees where employee_id between 100 and 120;3.in
select last_name, job_id from employees where job_id = ‘IT_PROT’ or job_id = ‘AD_VP’ job_id = ‘AD_PRES’;//繁琐
select last_name, job_id from employees where job_id in (‘IT_PROT’,‘AD_VP’,‘AD_PRES’);4.is null
select last_name, commission_pct from employees where commission_pct is null;//查询没奖金的
select last_name, commission_pct from employees where commission_pct is not null;//查询有奖金的
进阶3:排序查询
语法: select 查询列表③ from 表① [where 筛选条件]② order by 排序列表[asc|desc]④;
特点:
- asc代表的是升序,desc代表的是降序。如果不写,默认是升序
- order by子句中可以支持单个字段、多个字段、表达式、函数、别名
- order by子句一般是放在查询语句的最后面,limit子句除外
举例:
#案例1:查询员工信息,要求工资从高到低排序
select * from employees order by salary desc;
select * from employees order by salary; #升序#添加筛选条件
#案例2:查询部门编号>=90的员工信息,按入职时间的先后进行排序
SELECT * FROM employees WHERE department_id >= 90 ORDER BY hiredate ASC;#按表达式排序
#案例3:按年薪的高低显示员工的信息和年薪
SELECT , salary12*(1+IFNULL(commission_pct,0)) AS 年薪 FROM employees ORDER BY salary12(1+IFNULL(commission_pct,0)) DESC;#按别名排序
#案例4:按年薪的高低显示员工的信息和年薪
SELECT , salary12*(1+IFNULL(commission_pct,0)) AS 年薪 FROM employees ORDER BY 年薪 DESC;#按函数排序
#案例5:按姓名的长度显示员工的姓名和工资
SELECT LENGTH(last_name) 字节长度 , last_name, salary FROM employees ORDER BY 字节长度 DESC;#按多个字段排序
#案例6:查询员工信息,要求先按工资排序,再按员工编号排序
SELECT * FROM employees ORDER BY salary DESC, employee_id ASC;
进阶4:常见函数
功能:类似于Java中的方法,将一组逻辑语句封装在方法体中,对外暴露方法名
好处:隐藏了实现细节、提高了代码的重用性
调用:select 函数名(实参列表) [from 表];
特点:叫什么(函数名)、干什么(函数功能)
分类:
-
单行函数(传一个参数,返回一个值)
-
分类
-
字符函数:length、concat、substr、instr、trim、upper、lower、lpad、rpad、replace
-
数学函数:round、ceil、floor、truncate、mod
-
日期函数:now、curdate、curtime、year、month、monthname、day、hour、minute、second、str_to_date、date_format
-
datediff(‘a’,‘b’):返回日期a与日期b相差多少天
SELECT DATEDIFF(NOW(),‘1999-12-21’);
-
其他函数:version、database、user、password(‘字符’):返回该字符的密码形式、md5(‘字符’):返回该字符的md5的加密形式
-
控制函数:if、case
-
-
-
分组函数(传进一组值,返回一个值)
-
功能:做统计使用,又称为统计函数、聚合函数
-
分类:
- sum 求和、avg 平均值、max 最大值、min 最小值、count 计算值为非空的个数
-
特点:
-
sum、avg一般用于处理数值型
max、min、count可以处理任何类型
-
分组函数是否忽略null值
以上函数都忽略null值
-
可以和distinct搭配实现去重的运算
-
count函数的单独介绍
一般使用count(*)用作统计行数
-
和分组函数一同查询的字段要求是group by后的字段
-
-
单行函数举例
#一、字符函数
#1.length() 获取参数值的字节个数
SELECT LENGTH(‘john’); #4
SELECT LENGTH(‘张三丰hahaha’)#15 汉字要看具体的编码方式#2.concat() 拼接字符串
SELECT CONCAT(last_name,’_’,first_name) 姓名 FROM employees;#3.upper()、lower()
SELECT UPPER(‘john’);
SELECT LOWER(‘john’);
#实例:将姓大小,名小写,拼接
SELECT CONCAT(UPPER(last_name),LOWER(first_name)) 姓名 FROM employees;#4.substr()/substring()
#注意:索引从1开始
#截取从指定索引处后面所有字符
SELECT SUBSTR(‘李莫愁爱上了陆展元’,7) out_put;
#截取从指定索引处指定字符长度的字符
SELECT SUBSTR(‘李莫愁爱上了陆展元’,1,3) out_put;
#案例:姓名中首字符大写,其他字符小写然后用_拼接,显示出来
SELECT CONCAT(UPPER(SUBSTR(last_name,1,1)),’_’,LOWER(SUBSTR(last_name,2))) FROM employees;#5.instr() 返回子串第一次出现的索引,如果找不到返回0
SELECT INSTR(‘杨不悔殷六侠爱上了殷六侠’,‘殷六侠’) AS ‘index’; #4#6.trim() 去除首位指定的字符,默认去除的是空格
SELECT LENGTH(TRIM(’ 张翠花 ')) AS out_put;#三个汉字,字节长9SELECT TRIM(‘a’ FROM ‘aaaaa翠花aaaaaa张aaa’) AS out_put; #翠花aaaaaa张
SELECT TRIM(‘aa’ FROM ‘aaaaa翠花aaaaaa张aaa’) AS out_put; #a翠花aaaaaa张a 因他是按照连续的aa匹配的#7.lpad() 用指定的字符实现左填充指定长度(填充后总长度为指定的长度)
SELECT LPAD(‘殷素素’,10,’*’) AS out_put; #******殷素素
SELECT LPAD(‘殷素素’,2,’’) AS out_put; #殷素#8.rpad() 用指定的字符实现左填充指定长度(填充后总长度为指定的长度)
SELECT RPAD(‘殷素素’,12,‘ab’) AS out_put; #殷素素ababababa
SELECT RPAD(‘殷素素’,2,’*’) AS out_put; #殷素#9.replace() 替换
SELECT REPLACE(‘周芷若周芷若张无忌爱上了周芷若’,‘周芷若’,‘赵敏’) AS out_put;
#二、数学函数
#1.round() 四舍五入
SELECT ROUND(1.23); # 1
SELECT ROUND(1.65); # 2
SELECT ROUND(-1.23); # -1
SELECT ROUND(-1.65); # -2
SELECT ROUND(1.567,2); #重载函数,保留两位小数 1.57#2.ceil() 向上取整 返回>=该参数的最小整数
SELECT CEIL(-1.02); #-1#3.floor() 向下取整,返回<=该参数的最大整数
SELECT FLOOR(-9.99);#-10#4.truncate() 截断
SELECT TRUNCATE(1.65,1);#1.6 小数点后保留一位,不会产生近位,直接截断#5.mod() 取余
#mod(a,b) : a - a/b * b
#mod(-10,-3): -10 - (-10)/(-3) * (-3) = -1
SELECT MOD(10,-3); # 1
SELECT 1 % 3; # 1
#三、日期函数
#1.now 返回当前系统日期+时间
SELECT NOW();#2.curdate 返回当前的日期,不包含时间
SELECT CURDATE();#3.cutrime 返回当前的时间,不包含日期
SELECT CURTIME();#可获取指定的部分,年、月、日、小时、分钟、秒
SELECT YEAR(NOW()) 年; #2020
SELECT YEAR(‘1999-12-21’) 年; #1999
SELECT YEAR(hiredate) 年 FROM employees;
SELECT MONTH(NOW()) 月; #3
SELECT MONTHNAME(NOW()) 月; #March#4.str_to_date:将日期格式的字符转换成指定格式的日期
SELECT STR_TO_DATE(‘1998-3-2’,’%Y-%c-%d’) AS out_put;
#查询入职日期为 1992-4-3的员工的信息
SELECT * FROM employees WHERE hiredate = STR_TO_DATE(‘4-3 1992’, ‘%c-%d %Y’);#5.date_format:将日期转换成字符
SELECT DATE_FORMAT(NOW(),’%y年%m月%d日’) AS out_put;
#查询有奖金的员工名和入职日期(xx月/xx日 xx年)
SELECT last_name, DATE_FORMAT(hiredate,’%m月/%d日 %y年’) 入职日期 FROM employees WHERE commission_pct IS NOT NULL;#四、其他函数
SELECT VERSION)_; #版本
SELECT DATABASE(); #查看当前数据库
SELECT USER(); #查看当前用户#五、流程控制函数
#1.if函数:if else 的效果if(条件表达式,表达式1,表达式2):如果条件表达式成立,返回表达式1,否则返回表达式2
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;) (就是Java的default)
…
end
SELECT salary 原始工资, department_id,
CASE department_id
WHEN 30 THEN salary1.1
WHEN 40 THEN salary1.2
WHEN 50 THEN salary*1.3
ELSE salary
END AS 新工资
FROM employees;#2.case函数的使用二:类似于 多重if (判断区间)
#语法:
case
when 常量1 then 要显示的值1(或语句1;)
when 常量2 then 要显示的值2(或语句2;)
…
else 要显示的值n(或语句n;)
end
SELECT salary 工资,
CASE
WHEN salary > 20000 THEN ‘A’
WHEN salary > 15000 THEN ‘B’
WHEN salary > 10000 THEN ‘C’
ELSE ‘D’
END 等级
FROM employees;
分组函数举例
#1.简单的使用
SELECT SUM(salary) FROM employees;
SELECT AVG(salary) FROM employees;
SELECT MAX(salary) FROM employees;
SELECT MIN(salary) FROM employees;
SELECT COUNT(salary) FROM employees;
SELECT SUM(salary) 总和, ROUND(AVG(salary),2) 平均, MAX(salary) 最高, MIN(salary) 最低,COUNT(salary) 个数 FROM employees;#2.参数支持哪些类型
SELECT SUM(hiredate), AVG(hiredate) FROM employees;#3.是否忽略null
SELECT SUM(commission_pct), AVG(commission_pct), SUM(commission_pct)/35, SUM(commission_pct)/107 FROM employees;
SELECT MAX(commission_pct), MIN(commission_pct) FROM employees;
SELECT COUNT(commission_pct) FROM employees;#4.和distinct搭配
SELECT SUM(DISTINCT salary), SUM(salary) FROM employees;
SELECT COUNT(DISTINCT salary) FROM employees;#5.count函数的详细介绍
SELECT COUNT(salary) FROM employees;
SELECT COUNT() FROM employees; #用来统计行数
SELECT COUNT(‘任意值’) FROM employees; #等于加了一列1,统计1的个数。实际上还是统计行数。参数可以是任意数值
#效率
MYISAM存储引擎下,count()的效率高
INNODB存储引擎下,count(*)和count(1)的效率差不多,比count(字段)要高一些#6.和分组函数一同查询的字段有限制
SELECT AVG(salary),employee_id FROM employees#虽不报错,但也无意义
进阶5:分组查询
引入:要求查询每个部门的平均工资
可以使用group by子句将表中的数据分成若干组
语法:
SELECT 分组函数,列(要求出现再(GROUP by的后面)
FROM 表
[WHERE 筛选条件]
GROUP BY 分组的列表
[ORDER BY 子句]
注意:查询列表必须特殊,要求是分组函数和group by后出现的字段
特点:
-
1.分组查询中的筛选条件分为两类
数据源 位置 关键字 分组前筛选 原始表 group by子句的前面 where 分组后筛选 分组后的结果集 group by子句的后面 having - 分组函数做条件肯定放在having子句中
-
要想实现一行一列的结果集与多行一列的结果集依次比较,可以写在having里
- 能用分组前筛选的,就优先考虑使用分组前筛选,考虑性能问题
#案例1:查询每个工种的最高工资
SELECT MAX(salary),job_id FROM employees GROUP BY job_id;#案例2:查询每个位置上的部门个数
SELECT COUNT(*),location_id FROM departments GROUP BY location_id;#添加分组前筛选条件
#案例1:查询邮箱中包含a字符的,每个部门的平均工资
SELECT email, department_id, AVG(salary) FROM employees WHERE email LIKE ‘%a%’ GROUP BY department_id;
#案例2:查询有奖金的每个领导手下员工的最高工资
SELECT MAX(salary), manager_id FROM employees WHERE commission_pct IS NOT NULL GROUP BY manager_id;#添加分组后的筛选条件
#案例1:哪个部门的员工个数>2
#①查询每个部门的员工个数
SELECT COUNT(),department_id
FROM employees
GROUP BY department_id;
#②根据①的结果进行筛选,查询哪个部门的员工个数>2 这是分组后的筛选,所以要写在having里
SELECT COUNT(),department_id
FROM employees
GROUP BY department_id
HAVING COUNT(*)>2;#案例2:查询每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
#①查询每个工种有奖金的员工的最高工资
SELECT MAX(salary),job_id
FROM employees
GROUP BY job_id;
#②根据①的结果继续筛选,最高工资>12000
SELECT job_id, MAX(salary)
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id
HAVING MAX(salary) > 12000;#案例3:查询领导编号>102的每个领导手下的最低工资>5000的领导编号是哪个,以及其最低工资
#①查询每个领导手下的员工固定最低工资
SELECT MIN(salary),manager_id
FROM employees
GROUP BY manager_id;
#②在①的基础上查出领导编号>102
SELECT MIN(salary), manager_id
FROM employees
WHERE manager_id > 102
GROUP BY manager_id
#③添加条件:最低工资>5000
SELECT MIN(salary), manager_id
FROM employees
WHERE manager_id > 102
GROUP BY manager_id
HAVING MIN(salary) > 5000;
- 2.group by子句支持单个字段分组,多个字段分组(多个字段之间用逗号隔开没有顺序要求),表达式或函数(用的较少)
- 3.也可以添加排序(排序放在整个分组查询的最后)
#添加分组后的筛选条件
#案例1:哪个部门的员工个数>2
#①查询每个部门的员工个数
SELECT COUNT(),department_id
FROM employees
GROUP BY department_id;
#②根据①的结果进行筛选,查询哪个部门的员工个数>2 这是分组后的筛选,所以要写在having里
SELECT COUNT(),department_id
FROM employees
GROUP BY department_id
HAVING COUNT(*)>2;
#案例2:查询每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
#①查询每个工种有奖金的员工的最高工资
SELECT MAX(salary),job_id
FROM employees
GROUP BY job_id;
#②根据①的结果继续筛选,最高工资>12000
SELECT job_id, MAX(salary)
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY job_id
HAVING MAX(salary) > 12000;
#案例3:查询领导编号>102的每个领导手下的最低工资>5000的领导编号是哪个,以及其最低工资
#①查询每个领导手下的员工固定最低工资
SELECT MIN(salary),manager_id
FROM employees
GROUP BY manager_id;
#②在①的基础上查出领导编号>102
SELECT MIN(salary), manager_id
FROM employees
WHERE manager_id > 102
GROUP BY manager_id
#③添加条件:最低工资>5000
SELECT MIN(salary), manager_id
FROM employees
WHERE manager_id > 102
GROUP BY manager_id
HAVING MIN(salary) > 5000;#按表达式或函数分组
#案例:按员工姓名的长度分组,查询每一组的员工的个数,筛选员工个数>5的有哪些
#①查询每个长度的员工个数
SELECT COUNT() ,LENGTH(last_name) len_name
FROM employees
GROUP BY LENGTH(last_name);
#②添加筛选条件
SELECT COUNT() ,LENGTH(last_name) len_name
FROM employees
GROUP BY len_name #length(last_name)
HAVING COUNT(*)>5;#按多个字段分组
#案例:查询每个部门每个工种的员工的平均工资
SELECT AVG(salary),department_id,job_id
FROM employees
GROUP BY department_id,job_id;#添加排序
#案例:查询每个部门每个工种的员工的平均工资,并且按平均工资的高低显示
SELECT AVG(salary),job_id,department_id
FROM employees
WHERE department_id IS NOT NULL
GROUP BY department_id, job_id
HAVING AVG(salary) >10000
ORDER BY AVG(salary) DESC;
进阶6:连接查询
含义:又称多表查询,当查询的字段来自多个表时,就会用到连接查询
笛卡尔乘积现象:表1有m行,表2有n行,结果=m*n行
发生原因:没有有效的连接条件
如何避免:添加有效的来连接条件
分类:
- 内连接 inner
- 等值连接
- 多表等值连接的结果为多表的交集部分
- n表连接,至少需要n-1个连接条件
- 多表的顺序没有要求
- 一般需要为多表起别名
- 可以搭配前面介绍的所有的子句使用,比如排序、分组、筛选
- 非等值连接
- 自连接
- 等值连接
- 外连接
- 左外连接 left[outer]
- 右外连接 right[outer]
- 全外连接 full[outer]
- 交叉连接 cross(其实就是笛卡儿积)
内连接(sql92语法)
等值连接
语法:
select 查询列表
from 表1 别名,表2 别名
where 表1.key = 表2.key
[and 筛选条件]
[group by 分组字段]
[having 分组后的筛选]
[order by 排序字段]
示例:
#1、等值连接(连接条件是等于号)
#案例1:查询员工名和对应的部门名
SELECT last_name,department_name
FROM employees,departments
WHERE employees.department_id
= departments.department_id
;#2、为表起别名
#好处:提高语句的简洁度、区分多个重名的字段
#注意:如果为表起了别名,则查询的字段就不能使用原来的表名去限定#查询员工名、工种号、工种名
SELECT e.last_name, e.job_id, j.job_title
FROM employees e , jobs j
WHERE e.job_id
= j.job_id
;#3、可以筛选吗
#案例1:查询有奖金的员工名、部门名
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;
#案例2:查询城市名中第二个字符为o的部门名和城市名
SELECT department_name,city
FROM departments d, locations l
WHERE d.location_id
= l.location_id
AND city LIKE ‘_o%’;#4、可以加分组吗?
#案例1:查询每个城市的部门个数
SELECT COUNT(*) 个数,city
FROM departments d, locations l
WHERE d.location_id
= l.location_id
GROUP BY city;
#案例2:查询有奖金的每个部门的部门名和部门的领导编号和该部门的最低工资
SELECT d.manager_id,commission_pct,department_name,MIN(salary)
FROM employees e, departments d
WHERE e.department_id
= d.department_id
AND commission_pct IS NOT NULL
GROUP BY department_name,d.manager_id;#5、可以加排序?
#可以
非等值连接
#非等值连接
#案例:查询员工的工资和工资级别
SELECT salary,grade_level
FROM employees e, job_grades g
WHERE salary BETWEEN q.lowest_sal AND q.highest_sal;
自连接
#自连接
#案例:查找员工名和领导的名称
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
;
内连接(sql99语法)
语法:
select 查询列表
from 表1 别名
【inner】join 表2 别名 on 连接条件
where 筛选条件
group by 分组列表
having 分组后的筛选
order by 排序列表
limit 子句
from 表1 别名
【inner】 join 表2 别名
on 连接条件;
特点:
- 添加排序、分组、筛选
- inner可以省略
- 筛选条件放在where后面,连接条件放在on后面,提高分离性,便于阅读
- inner join连接和sql92语法中的等值连接效果是一样的,都是查询多表的交集
等值连接
#查询员工名、部门名
SELECT last_name,department_name
FROM departments d
INNER JOIN employees e
ON e.department_id
= d.department_id
#查询名字中包含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%’;#查询员工名、部门名、工种名,并按部门名降序
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 department_name DESC;
非等值连接
#案例:查询员工的工资和工资级别
SELECT salary, grade_level
FROM employees e
JOIN job_grades g
ON e.salary BETWEEN g.lowest_sal AND g.highest_sal;
外连接(sql99语法)
应用场景:用于查询一个表中有,另一个表没有的记录
语法:
select 查询列表
from 表1 别名
left|right|full【outer】join 表2 别名 on 连接条件
where 筛选条件
group by 分组列表
having 分组后的筛选
order by 排序列表
limit 子句
特点:
-
外连接的查询结果为主表中的所有记录,如果从表中和他匹配的,则显示匹配的值,如果从表中没有和它匹配的,则显示null,外连接查询结果=内连接结果+主表中有而从表中没有的记录
-
左外连接,left join左边的是主表
右外连接,right join右边的是主表
-
左外和右外交换两个表的顺序,可以实现同样的效果
-
一般来说你要查询的主要信息在那个表,那个表就设置为主表
-
全外连接 = 内连接的结果 + 表1中有但表2没有的 + 表2中有但表1没有的
左外连接
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;
进阶7:子查询
含义:嵌套在其他语句中的select语句,称为子查询或内查询
外部的查询语句,称为主查询或外查询
分类:
- 按子查询出现的位置:
- select后面
- 仅仅支持标量子查询
- from后面
- 支持表子查询
- where或having后面 ★
- 标量子查询(单行)
- 列子查询(多行)
- 行子查询(用的较少)
- exists后面(又称相关子查询)
- 表子查询
- select后面
- 按结果集的行列数不同:
- 标量子查询(结果集只有一行一列)
- 列子查询(结果集只有一列多行)
- 行子查询(结果集有一行多列)
- 表子查询(结果集一般为多行多列)
一、where或having后面
1.标量子查询(单行子查询)
2.列子查询(单列子查询)
3.行子查询(多列多行)
特点:
-
子查询放在小括号内
-
子查询一般放在条件的右侧
-
标量子查询,一般搭配着单行操作符使用:> < >= <= = <>
列子查询:一般搭配着多行操作符使用:in、any/some、all
-
子查询的执行优先于主查询执行,主查询的条件用到了子查询的结果
1.标量子查询
#标量子查询
#案例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和salary,要求salary=①
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
#③在②基础上筛选,满足min(salary)>①
SELECT department_id, MIN(salary)
FROM employees
GROUP BY department_id
HAVING MIN(salary) > (
SELECT MIN(salary)
FROM employees
WHERE department_id = 50
);#非法使用标量子查询
SELECT department_id, MIN(salary)
FROM employees
GROUP BY department_id
HAVING MIN(salary) > ( # 单行操作符>一般搭配标量子查询
#子查询的结果是多行的
SELECT salary
FROM employees
WHERE department_id = 50
);
2.列子查询(多行子查询)
返回多行
使用多行比较操作符
-
IN / NOT IN : 等于(不等于)列表中的任意一个
-
ANY|SOME :和子查询返回的某一个值比较(用的少)
a > any(15,3,33,42):代表a大于这四个数的任意一个值就行
可以用 a > min(15,3,33,42)语句代替
-
ALL :和子查询返回的所有值比较
-
any和all都可用min或max代替
#列子查询(多行子查询)
#案例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 (只有在结果集中比较才能等价)
SELECT last_name
FROM employees
WHERE department_id =ANY (
SELECT DISTINCT department_id
FROM
departments
WHERE location_id IN (1400,1700) #这里就不能等价
);#案例2:返回其他工种中比job_id工种为IT_PROG部门任一工资低的员工的员工号、姓名、job_id 以及salary
#①查询job_id为IT_PROG部门任一工资
SELECT DISTINCT salary
FROM employees
WHERE job_id = ‘IT_PROG’;
#②查询员工号、姓名、job_id 以及salary,salary < ①的任意一个
SELECT last_name,employee_id,job_id,salary
FROM employees
WHERE salary < ANY(
SELECT DISTINCT salary
FROM employees
WHERE job_id = ‘IT_PROG’
) AND job_id <> ‘IT_PROG’;
#或者
SELECT last_name,employee_id,job_id,salary
FROM employees
WHERE salary < (
SELECT MAX(salary)
FROM employees
WHERE job_id = ‘IT_PROG’
) AND job_id <> ‘IT_PROG’;#案例2:返回其他工种中比job_id工种为IT_PROG部门所有工资低的员工的员工号、姓名、job_id 以及salary
SELECT last_name,employee_id,job_id,salary
FROM employees
WHERE salary < ALL(
SELECT DISTINCT salary
FROM employees
WHERE job_id = ‘IT_PROG’
) AND job_id <> ‘IT_PROG’;
#或者
SELECT last_name,employee_id,job_id,salary
FROM employees
WHERE salary < (
SELECT MIN(salary)
FROM employees
WHERE job_id = ‘IT_PROG’
) AND job_id <> ‘IT_PROG’;
3.行子查询
#行子查询(结果集一行多列或多行多列)
#案例:查询员工编号最小并且工资最高的人的信息
#①查编号最小
SELECT MIN(employee_id)
FROM employees;
#②查工资最高
SELECT MAX(salary)
FROM employees;
#③查员工信息
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
);
二、select后面
#案例:查询每个部门的员工个数
SELECT d.* , (
SELECT COUNT(*)
FROM employees e
WHERE d.department_id
= e.department_id
) 个数
FROM departments d;
三、from后面
将子查询结果充当一张表,要求必须起别名
四、exits后面(相关子查询)
语法:exists(完整的查询语句)
结果:1或0(查询的结果是否有结果)
SELECT EXISTS(SELECT employee_id FROM employees) #1
SELECT EXISTS(SELECT employee_id FROM employees WHERE salary = 3000000) #0
进阶8:分页查询
应用场景:当要显示的数据,一页显示不全,需要分页提交sql请求
语法:
select 查询列表 ⑦
from 表 ①
【连接类型(inner…) join 表2 ②
on 连接条件 ③
where 筛选条件 ④
group by 分组字段 ⑤
having 分组后的筛选 ⑥
order by 排序的字段 】 ⑧
limit 【offset ,】 size; ⑨
offset:要显示条目的其实索引**(起始索引从0开始)**
size:要显示的条目个数
特点:
-
limit语句放在查询语句的最后
-
公式:要显示的页数是page,每页的条目数size
select 查询列表
from 表
limit (page-1) * size , size;
案例:
#案例1:查询前五条员工信息
SELECT * FROM employees LIMIT 0,5;
SELECT * FROM employees LIMIT 5; # 如果从第一条开始可以省略offset#案例2:查询第11条到第25条数据
SELECT * FROM employees LIMIT 10,15;#案例3:有奖金的员工信息,并且工资较高的前10名显示出来
SELECT *
FROM employees
WHERE commission_pct IS NOT NULL
ORDER BY salary DESC
LIMIT 10;
进阶9 :联合查询
union:联合、合并:将多条查询语句的结果合并成一个结果
语法:
查询语句1
union
查询语句2
union
……
应用场景:当查询的结果来自多个表,且这多个表没有直接的连接关系时,但查询的信息一直时,推荐使用联合查询
特点:
- 要求多条查询语句的查询的列数是一致的
- 要求多条查询语句的查询的每一列的类型和顺序最好一致
- union关键字默认去重,如果使用union all 可以包含重复项
#联合查询
#引入:查出部门编号>90或邮箱包含a的员工信息
SELECT * FROM employees WHERE email LIKE ‘%a%’ OR department_id > 90;
#或者
SELECT * FROM employees WHERE email LIKE ‘%a%’
UNION
SELECT * FROM employees WHERE department_id>90;#union all
SELECT id,cname FROM t_ca WHERE csex=‘男’
UNION ALL
SELECT t_id,tname FROM t_ua WHERE tGender=‘male’;