9.子查询

#DQL语言
##八、子查询
###1.什么是子查询?
####子查询的分类
按结果集的行列数不同分类:
     ·标量子查询(结果集只有一行一列,也称“单行子查询”)
     ·列子查询(结果集只有一列多行,也称“多行子查询”)
     ·行子查询(结果集只有一行多列)
     ·表子查询(结果集一般为多行多列)

按子查询出现的位置分类:    
     ·select后面:仅仅支持标量子查询;
     ·from后面:支持表子查询
     ·where或having后面:
          标量子查询(★)
          列子查询(★)
          行子查询
     ·exist后面:(也称为“相关子查询”)
          表子查询
          列子查询
          行子查询
          标量子查询

标了(★)的是最用的最多、最重要的子查询,下面我们一一介绍,先介绍最重要的!

###2.子查询语法
####①where或having后面的子查询

先说特点:
     a.子查询放在小括号内
     b.子查询一般放在条件的右侧
     c.标量子查询,一般搭配着单行操作符使用(这里的所谓单行操作符即指基本运算符,如:> < >= <= = <>)
     d.列子查询,则一般搭配着多行操作符使用(如:in、any/some、all)
     e.子查询的执行优先于主查询执行,主查询的条件用到了子查询的结果

#####⑴标量子查询(结果集为一行一列)

案例一:谁的工资比Abel高

分两步走:
Step1:查询Abel的工资

SELECT 
    salary 
FROM
    employees 
WHERE last_name = 'Abel'

结果如下↓
在这里插入图片描述
大家看,标量子查询的结果集的确只有一行一列吧~

Step2:查询工资比Step1结果高的员工信息

SELECT 
  * 
FROM
  employees 
WHERE salary > 
  (SELECT                              //接下来这几行都是Step1的语句
    salary 
  FROM
    employees 
  WHERE last_name = 'Abel') ;

OK,最终结果如下↓
在这里插入图片描述

案例二:返回job_id与141号员工相同,salary比143号员工多的员工姓名、job_id和工资
这个我就不一一按步骤走了,大家可以自己去过一遍,直接抛出答案

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
);

案例三:返回公司工资最少的员工的last_name,job_id和salary

SELECT last_name,job_id,salary
FROM employees
WHERE salary=(
SELECT
	MIN(salary)
FROM
	employees
	
);

那么,前三个案例都是在where后面跟着的标量子查询,接下来的案例就是跟在having后面的标量子查询

案例四:查询最低工资大于50号部门最低工资的部门id和其最低工资

SELECT department_id,MIN(salary)
FROM employees
GROUP BY department_id
HAVING MIN(salary)>(
SELECT MIN(salary)
FROM employees
WHERE department_id =50
);

#####⑵列子查询(结果集为一列多行)
我们先前讲特点的时候已经说过:列子查询,则一般搭配着多行操作符使用(如:in、any/some、all)
这三者区别如下:

操作符含义
IN/NOT IN等于列表中的任意一个
ANY/SOME和子查询返回的某一个值进行比较
ALL和子查询返回的所有值进行比较

具体来说
    a.若字段a in(10,20,30)表示只要a为10或20或30之间的某一个既可以成功查询,那么现在针对于列子查询而言,in()括号里面的参数只不过是替换成了一个列表而已,即“一列多行”;
    b.若字段a<ANY(10,20,30)的话表示a比ANY参数中任意一个数小即可成功查询,“比其中任意一个数小”相当于a不能超过参数列表的最大值,我们也可以用a<max(10,20,30)表示;同理,若字段a>ANY(10,20,30),也即是意味着a>min(10,20,30) //这一点很容易混淆,希望大家仔细琢磨琢磨
    c.若字段a>ALL(10,20,30)即是意味着a>max(10,20,30);若字段a<ALL(10,20,30)即是意味着a<min(10,20,30),这应该很好理解

下面请看案例

案例一:返回location_id是1400或1700的部门中的所有员工姓名
分两步走:

Step1 →查询location_id是1400或1700的部门

SELECT DISTINCT department_id                //一般为了去重我们加一下distinct
FROM departments
WHERE location_id IN(1400,1700)

结果如下
在这里插入图片描述
大家可见,结果集显然是一列多行吧!

Step2

SELECT last_name
FROM employees
WHERE department_id IN(                      //只要部门id是Step1结果中的某一个即可
	SELECT DISTINCT department_id
	FROM departments
	WHERE location_id IN(1400,1700)
);

PS:语句中的IN可以用“=ANY”替换!(道理大家简单想想就好了)

案例二:返回其他job_id中比job_id为’IT_PROG’工种任一工资低的员工的:工号、姓名、job_id以及salary

分两步走:
Step1 →查询job_id为’IT_PROG’工种员工的工资

SELECT DISTINCT salary
FROM employees
WHERE job_id='IT_PROG'

Step1

SELECT job_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';         //注意是返回其他job_id...所以我们添加一个筛选条件

那假如我们不用ANY去表示,我们也可以这样表示

SELECT job_id,last_name,job_id,salary
FROM employees
WHERE salary <(              //去掉ANY
SELECT MAX(salary)           //小于其中任意一个值,那我们大于最大值好不好?明显不好,那我们只需要小于最大值是不是就可以了~
FROM employees
WHERE job_id='IT_PROG'
)AND job_id<>'IT_PROG';

案例三:返回其他job_id中比job_id为’IT_PROG’工种所有工资低的员工的:工号、姓名、job_id以及salary

(这题显然用ALL就解决啦,和上题差不多,查询语句如下)

SELECT job_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';

同样我们不用ALL的话也可以这样写

SELECT job_id,last_name,job_id,salary
FROM employees
WHERE salary <(
SELECT min(salary)             //小于最小值ok啦
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
);

结果如下
在这里插入图片描述
显然结果集是一行多列吧~

PS:大家也可以看到这样做存在局限性,那就是只有当employee_id和salary同时后面跟的是同一种运算符时才行,这道题是“=”。

####②select后面的子查询(仅支持标量子查询,结果集只能为一行一列)

直接给出案例
案例一:查询每个部门的员工个数(这道题也可以用分组查询,但我们在这儿用子查询解决)

SELECT department_id,(
SELECT COUNT(*)                         //注意:select后的子查询的结果集必须是一行一列!!!
FROM employees e 
WHERE e.`department_id`=d.`department_id`
) 个数
FROM departments d;

案例二:查询员工号=102的部门名

SELECT (
SELECT department_name
FROM departments d
INNER JOIN employees e
ON d.department_id=e.department_id
WHERE e.employee_id=102
) 结果;

####③from后面的子查询(支持表子查询)

from后面的子查询:将我们的子查询结果充当一张表,要求必须起别名!!!
以例题为例讲解from后面的子查询:

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

两步走
Step1:查询每个部门的平均工资

SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id;

执行结果如下
在这里插入图片描述
那么大家可以看到我们得到了一个新的表,这非常关键

Step2:查询步骤1生成的表的工资等级

SELECT 新表.*,g.grade_level
FROM (
	SELECT AVG(salary) 平均工资,department_id
	FROM employees
	GROUP BY department_id
) 新表                               //★★★一定要给新生成的表起别名!!!否则就不知道该表怎么表示
INNER JOIN job_grades g
ON 新表.平均工资 BETWEEN g.lowest_sal AND highest_sal;

ok啦,结果如下:
在这里插入图片描述

####④exist后面的子查询/相关子查询(支持表子查询、行子查询、列子查询、标量子查询)

先提前说一下,凡是能用exist子查询的,都可以用in(列子查询)去查

案例一:查询有员工的部门名

用exist

SELECT department_name
FROM departments d
WHERE EXISTS(
SELECT *
FROM employees e
WHERE e.`department_id`=d.`department_id`
);

假如用in怎么写?

SELECT department_name
FROM departments d
WHERE d.`department_id` IN(
SELECT department_id
FROM employees e
WHERE e.`department_id`=d.`department_id`
);

之所以说exist后面都支持表子查询、行子查询、列子查询、标量子查询这几种查询是因为它只返回有无结果,查到了就返回,查不到就不返回,所以和后面是什么类型的查询都无关

下面是子查询的几道例题

案例一:查询各部门中工资比本部门平均工资高的员工的员工号,姓名和工资

SELECT employee_id,last_name,salary
FROM employees e
INNER JOIN (
	SELECT AVG(salary) ag,department_id              //一定要为AVG(salary)起别名,否则无法判断是employees表中的还是“表”中的
	FROM employees m
	GROUP BY department_id
) 表
ON e.`department_id`=表.department_id 
WHERE e.salary > 表.ag;

案例二:查询和姓名中包含字母u的员工在相同部门的员工的员工名和姓名

SELECT DISTINCT employee_id,e.last_name
FROM employees e
INNER JOIN (
	SELECT department_id,last_name
	FROM employees 
	WHERE last_name LIKE '%u%' 
) tab
ON e.department_id=tab.department_id;

案例三:查询在部门的location_id为1700的部门工作的员工的员工号

SELECT e.employee_id
FROM employees e
WHERE department_id IN(
SELECT department_id
FROM departments d
WHERE d.`location_id`=1700
);

以上就是子查询的内容,下一节将介绍分页查询!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值