MySQL基础篇——第09章 子查询

MySQL基础篇——第09章 子查询

子查询指一个查询语句嵌套在另一个查询语句内部的查询

SQL 中子查询的使用大大增强了 SELECT 查询的能力,因为很多时候查询需要从结果集中获取数据,或者需要从同一个表中先计算得出一个数据结果,然后与这个数据结果(可能是某个标量,也可能是某个集合)进行比较

SELECT 查询语句中,除了 GROUP BYLIMIT 子句之外,其他位置都可以声明子查询!

1. 需求分析与问题解决

1.1 实际问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XsAWgtU7-1652660500305)(MySQL基础篇——第09章 子查询.assets/image-20220423012212008.png)]

# 方式一:自连接
SELECT e2.`last_name`, e2.`salary`
FROM employees e1 JOIN employees e2
ON e1.`last_name` = 'Abel'
WHERE e1.`salary` < e2.`salary`;

# 方式二:子查询
SELECT last_name, salary
FROM employees
WHERE salary > (
				SELECT salary
				FROM employees
				WHERE last_name = 'Abel'
				);

1.2 子查询的基本使用

  • 子查询的基本语法结构:
SELECT select_list
FROM table
WHERE expr operator (
					SELECT select_list
					FROM table
					......;
					);
  • 子查询(内查询)在主查询 (外查询)之前一次执行完成
  • 子查询(内查询)的结果被主查询(外查询)使用
  • 注意事项:
    • 子查询要包含在括号内
    • 将子查询放在比较条件的右侧
    • 单行操作符对应单行子查询,多行操作符对应多行子查询
    • SELECT 查询语句中,除了 GROUP BYLIMIT 子句之外,其他位置都可以声明子查询!

1.3 子查询的分类

1.3.1 单行子查询 vs 多行子查询

按子查询的结果返回一条还是多条记录,将子查询分为:单行子查询 vs 多行子查询

  1. 单行子查询:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rVkKWmmc-1652660500306)(MySQL基础篇——第09章 子查询.assets/image-20220423022354069.png)]

  1. 多行子查询:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-At05GNXd-1652660500307)(MySQL基础篇——第09章 子查询.assets/image-20220423022402888.png)]

1.3.2 相关(关联)子查询 vs 不相关(非关联)子查询

按内查询是否被执行多次,将子查询分为:相关(关联)子查询 vs 不相关(非关联)子查询

  1. 不相关子查询:子查询从数据表中查询了数据结果,如果这个数据结果只执行一次,且作为主查询的条件进行执行

    • 如:查询工资大于本公司平均工资的员工信息
  2. 相关子查询:子查询需要执行多次,即采用循环的方式,先从外部查询开始,每次都传入子查询进行查询,然后再将结果反馈给外部,这种嵌套的执行方式就称为相关子查询

    • 如:查询工资大于本部门平均工资的员工信息

2. 单行子查询

2.1 单行比较操作符

操作符含义
=equal to
>greater than
>=greater than or equal to
<less than
<=less than or equal to
<>(!=)not equal to

2.2 代码示例

子查询编写的技巧(或步骤):① 从里往外写;② 从外往里写

  • 举例1:查询工资大于149号员工工资的员工的信息
SELECT employee_id, last_name, salary
FROM employees
WHERE salary > (
				SELECT salary
				FROM employees
				WHERE employee_id = 149
				);
  • 举例2:返回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
                );
  • 举例3:返回公司工资最少的员工的last_name,job_id和salary
SELECT last_name, job_id, salary
FROM employees
WHERE salary = (
                SELECT MIN(salary)
                FROM employees
                );
  • 举例4:查询与141号员工的manager_id和department_id相同的其他员工的employee_id,manager_id,department_id
# 方式一:不成对比较
SELECT employee_id, manager_id, department_id   
FROM employees
WHERE manager_id = (
                    SELECT manager_id
                    FROM employees
                    WHERE employee_id = 141
                    )
AND department_id = (
                    SELECT department_id
                    FROM employees
                    WHERE employee_id = 141
                    )
AND employee_id <> 141;

# 方式二:成对比较
SELECT employee_id, manager_id, department_id   
FROM employees
WHERE (manager_id, department_id) = (
                                    SELECT manager_id, department_id
                                    FROM employees
                                    WHERE employee_id = 141
                                    )
AND employee_id <> 141;

2.3 HAVING 中的子查询

  1. 首先执行子查询

  2. 向主查询中的 HAVING 子句返回结果

  • 举例:查询最低工资大于50号部门最低工资的部门id和其最低工资
SELECT department_id, MIN(salary)
FROM employees
WHERE department_id IS NOT NULL
GROUP BY department_id
HAVING MIN(salary) > (
                     SELECT MIN(salary)
                     FROM employees
                     WHERE department_id = 50
                     );

2.4 CASE 中的子查询

  • 举例:查询员工的employee_id,last_name和location。其中,若员工department_id与location_id为1800的department_id相同,则location为’Canada’,其余则为’USA’
SELECT employee_id, last_name, (CASE department_id WHEN (
                                                        SELECT department_id 
                                                        FROM departments
                                                        WHERE location_id = 1800
                                                        )
                                                    THEN 'Canada'
                                                    ELSE 'USA' END) "location"
FROM employees;

2.5 单行子查询的空值情况

  • 举例:查询与last_name为’Haas’的员工相同的job_id的员工信息(last_name和job_id)

但是employees表中实际上并没有last_name为’Haas’的员工,即该子查询不返回任何记录(行)

SELECT last_name, job_id
FROM employees
WHERE job_id =  (
                SELECT job_id
                FROM employees
                WHERE last_name = 'Haas'
                );

2.6 非法使用子查询

  • 多行子查询使用单行比较符:该子查询按department_id分组,每个组都有一个MIN(salary)值,即返回的结果中有多条记录
SELECT employee_id, last_name
FROM employees
WHERE salary =  (
                SELECT MIN(salary)
                FROM employees
                GROUP BY department_id
                );

3. 多行子查询

  • 也称为集合比较子查询
  • 子查询返回的结果中包含多条记录(多行)
  • 使用多行比较操作符

3.1 多行比较操作符

操作符含义
IN等于列表中的任意一个
ANY / SOME需要和单行比较操作符一起使用,和子查询返回的某一个值比较(表示任意一个)
ALL需要和单行比较操作符一起使用,和子查询返回的所有值比较(表示所有)

3.2 代码示例

3.2.1 IN
  • 举例1:查询工资为每个部门最低工资的员工信息
SELECT employee_id, last_name
FROM employees
WHERE salary IN (
                SELECT MIN(salary)
                FROM employees
                GROUP BY department_id
                );
  • 举例2:查询与141号或174号员工的manager_id和department_id相同的其他员工的employee_id,manager_id,department_id
# 方式一:不成对比较
SELECT employee_id, manager_id, department_id   
FROM employees
WHERE manager_id IN (
                    SELECT manager_id
                    FROM employees
                    WHERE employee_id IN (141, 174)
                    )
AND department_id IN (
                    SELECT department_id
                    FROM employees
                    WHERE employee_id IN (141, 174)
                    )
AND employee_id NOT IN (141, 174);

# 方式二:成对比较
SELECT employee_id, manager_id, department_id   
FROM employees
WHERE (manager_id, department_id) IN (
                                        SELECT manager_id, department_id
                                        FROM employees
                                        WHERE employee_id IN (141, 174) 
                                        )
AND employee_id NOT IN (141, 174);
3.2.2 ANY(SOME) / ALL
  • 举例1:返回其它job_id中比job_id为 ‘IT_PROG’ 的部门任意一个员工的工资低的员工的员工号、姓名、job_id 以及salary
SELECT employee_id, last_name, job_id, salary
FROM employees
WHERE salary < ANY (
                    SELECT salary
                    FROM employees
                    WHERE job_id = 'IT_PROG'
                    )
AND job_id <> 'IT_PROG';
  • 举例2:返回其它job_id中比job_id为 ‘IT_PROG’ 的部门所有员工的工资都低的员工的员工号、姓名、job_id 以及salary
SELECT employee_id, last_name, job_id, salary
FROM employees
WHERE salary < ALL (
                    SELECT salary
                    FROM employees
                    WHERE job_id = 'IT_PROG'
                    )
AND job_id <> 'IT_PROG';
  • 举例3:查询平均工资最低的部门id
# 方式一:将最内部子查询的结果集作为一张临时表
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary) = (
                    SELECT MIN(avg_sal)
                    FROM (
                        SELECT AVG(salary) "avg_sal"
                        FROM employees
                        GROUP BY department_id		
                        ) dept_avg_sal
                    );
                    
# 方式二:使用ALL
SELECT department_id
FROM employees
GROUP BY department_id
# 循环遍历每一个部门(分组),判断当前部门的平均工资是否小于等于所有部门(包括自己)
HAVING AVG(salary) <= ALL (
                            SELECT AVG(salary)
                            FROM employees
                            GROUP BY department_id
                            );
                            
# 方式三:使用ORDER BY + LIMIT
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary) = (
                    SELECT AVG(salary) "avg_sal"
                    FROM employees
                    GROUP BY department_id
                    ORDER BY avg_sal ASC
                    LIMIT 0, 1
                    );

3.3 多行子查询的空值情况

# 子查询返回的结果中包含NULL,而NOT IN在执行时需要与列表中所有值进行比较,NULL值参与运算返回值也是NULL
SELECT last_name
FROM employees
WHERE employee_id NOT IN (
                        SELECT manager_id
                        FROM employees
                        );

4. 相关子查询

4.1 相关子查询执行流程

  • 如果子查询的执行依赖于外部查询,通常情况下都是因为子查询中使用到了主查询的表的列,并进行了条件关联,因此每执行一次外部查询,子查询都要重新计算一次,这样的子查询就称之为 关联子查询

  • 相关子查询按照一行接一行的顺序执行,主查询的每一行(每一条记录)都执行一次子查询

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pIPb8nAT-1652660500307)(MySQL基础篇——第09章 子查询.assets/image-20220423205541206.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PbSWO1kn-1652660500308)(MySQL基础篇——第09章 子查询.assets/image-20220423214626028.png)]

4.2 代码示例

  • 举例1:查询员工中工资大于本部门平均工资的员工的last_name,salary和其department_id
# 方式一:相关子查询
SELECT last_name, salary, department_id 
FROM employees e1
WHERE salary > (
    		   # 当对外部某一条记录(某一员工)进行判断时,将其部门id传进子查询中,此时子查询就会查询当前员工所在部门的平均工资
               SELECT AVG(salary)
               FROM employees e2
    		   # 子查询中使用到了主查询的表的列,并进行了条件关联
               WHERE department_id = e1.`department_id`
               );
               
# 方式二:用虚拟表进行多表查询:在 FROM 中使用子查询,
# 将子查询返回的结果集(每个部门的平均工资)作为一张虚拟表,进行多表查询
SELECT e.`last_name`, e.`salary`, e.`department_id`
FROM employees e, (
                    SELECT department_id, AVG(salary) "dept_avg_sal"
                    FROM employees
                    GROUP BY department_id
                    ) t_dept_avg_sal
WHERE e.`department_id` = t_dept_avg_sal.department_id
AND e.`salary` > t_dept_avg_sal.dept_avg_sal;

# 方式三:将方式二写成SQL99的形式,即使用JOIN ON
SELECT e.`last_name`, e.`salary`, e.`department_id`
FROM employees e JOIN (
                        SELECT department_id, AVG(salary) "dept_avg_sal"
                        FROM employees
                        GROUP BY department_id
                        ) t_dept_avg_sal
ON e.`department_id` = t_dept_avg_sal.department_id
WHERE e.`salary` > t_dept_avg_sal.dept_avg_sal;

FROM 子句中使用子查询:将子查询返回的结果集作为一张 临时的虚拟表 。该子查询要用 () 括起来,且必须给其取别名,作为该虚拟表的别名

  • 举例2:查询员工的id,salary,按照department_name 排序

(在 ORDER BY 中使用子查询)

SELECT employee_id, salary
FROM employees e
ORDER BY (
        SELECT department_name
        FROM departments d
    	# 子查询中使用到了主查询的表的列,并进行了条件关联
        WHERE e.`department_id` = d.`department_id`
        );
  • 举例3:若employees表中employee_id与job_history表中employee_id相同的数目不小于2,输出这些相同id的员工的employee_id,last_name和其job_id
SELECT e.`employee_id`, e.`last_name`, e.`job_id`
FROM employees e
WHERE 2 <= (
            SELECT COUNT(*)
            FROM job_history j
            WHERE j.`employee_id` = e.`employee_id`
            );

4.3 EXISTSNOT EXISTS 关键字

  1. 关联子查询通常也会和 EXISTS 操作符一起来使用,用来检查在子查询中是否存在满足条件的行

  2. 如果在子查询中不存在满足条件的行:

    • 条件返回 FALSE

    • 继续在子查询中查找

  3. 如果在子查询中存在满足条件的行:

    • 不在子查询中继续查找
    • 条件返回 TRUE
  4. NOT EXISTS 关键字表示如果不存在某种条件,则返回 TRUE,否则返回 FALSE

  • 举例1:查询公司管理者的employee_id,last_name,job_id,department_id信息
# 方式一:自连接
SELECT DISTINCT m.`employee_id`, m.`last_name`, m.`job_id`, m.`department_id`
FROM employees e JOIN employees m
ON e.`manager_id` = m.`employee_id`

# 方式二:多行子查询
SELECT employee_id, last_name, job_id, department_id
FROM employees
WHERE employee_id IN (
                    SELECT DISTINCT manager_id
                    FROM employees
                    );
			
# 方式三:使用EXISTS
SELECT employee_id, last_name, job_id, department_id
FROM employees e1
# 当对外部某一条记录进行判断时,在子查询中查找是否有满足条件的行,如果有,中断这一次子查询,且返回TRUE(1)
WHERE EXISTS (
			SELECT *
    		FROM employees e2
			WHERE e1.`employee_id` = e2.`manager_id`
			);
  • 举例2:查询departments表中,不存在于employees表中的部门的department_id和department_name
# 方式一:7种JOIN连接的左中图
SELECT d.`department_id`, d.`department_name`
FROM departments d LEFT JOIN employees e
ON d.`department_id` = e.`department_id`
WHERE e.`department_id` IS NULL;

# 方式二:使用NOT EXISTS
SELECT department_id, department_name
FROM departments d
WHERE NOT EXISTS (
                SELECT *
                FROM employees e
                WHERE d.`department_id` = e.`department_id` 
                );

4.4 相关更新

  • 使用相关子查询依据一个表中的数据更新另一个表的数据:
UPDATE table1 alias1
SET column = (
    		SELECT expression
            FROM table2 alias2
            WHERE alias1.column = alias2.column
			);

  • 举例:在employees中增加一个department_name字段,数据为员工对应的部门名称
# 1)
ALTER TABLE employees
ADD(department_name VARCHAR2(14));

# 2)
UPDATE employees e
SET department_name = (
    				SELECT department_name
                    FROM departments d
                    WHERE e.department_id = d.department_id
					);

4.5 相关删除

  • 使用相关子查询依据一个表中的数据删除另一个表的数据
DELETE FROM table1 alias1
WHERE column operator (
                    SELECT expression
                    FROM table2 alias2
                    WHERE alias1.column = alias2.column
					);
  • 举例:删除表employees中,其与emp_history表皆有的数据
DELETE FROM employees e1
WHERE employee_id in (
                    SELECT employee_id
                    FROM emp_history e2
                    WHERE e1.employee_id = e2.employee_id
                    );

5. 抛一个思考题

**问题:**谁的工资比Abel的高?

解答:

# 方式一:自连接
SELECT e2.`last_name`, e2.`salary`
FROM employees e1 JOIN employees e2
ON e1.`last_name` = 'Abel'
WHERE e1.`salary` < e2.`salary`;

# 方式二:子查询
SELECT last_name, salary
FROM employees
WHERE salary > (
				SELECT salary
				FROM employees
				WHERE last_name = 'Abel'
				);

**问题:**以上两种方式有好坏之分吗?

**解答:**自连接方式好!

题目中可以使用子查询,也可以使用自连接。一般情况建议使用自连接,因为在许多 DBMS 的处理过程中,对于自连接的处理速度要比子查询快得多。

可以这样理解:子查询实际上是通过未知表进行查询后的条件判断,而自连接是通过已知的自身数据表进行条件判断,因此在大部分 DBMS 中都对自连接处理进行了优化。

6. 课后练习

# 1.查询和Zlotkey相同部门的员工姓名和工资
# 方法一:自连接
SELECT e2.`last_name`, e2.`salary`
FROM employees e1 JOIN employees e2
ON e1.`last_name` = 'Zlotkey'
WHERE e1.`department_id` = e2.`department_id`;

# 方法二:子查询(为确保没有同名的员工,使用`IN`替换`=`)
SELECT last_name, salary
FROM employees
WHERE department_id IN (
			SELECT department_id
			FROM employees
			WHERE last_name = 'Zlotkey'
			); 
		
# 2.查询工资比公司平均工资高的员工的员工号,姓名和工资。
SELECT employee_id, last_name, salary
FROM employees
WHERE salary > (
		SELECT AVG(salary)
		FROM employees
		);


# 3.选择工资大于所有JOB_ID = 'SA_MAN'的员工的工资的员工的last_name, job_id, salary
# 方法一:使用ALL(多行子查询)
SELECT last_name, job_id, salary
FROM employees
WHERE salary > ALL (
		SELECT salary
		FROM employees
		WHERE job_id = 'SA_MAN'
		);

# 方法二:单行子查询
SELECT last_name, job_id, salary
FROM employees
WHERE salary > (
		SELECT MAX(salary)
		FROM employees
		WHERE job_id = 'SA_MAN'
		);

# 4.查询和姓名中包含字母u的员工在相同部门的员工的员工号和姓名
SELECT employee_id, last_name
FROM employees
WHERE department_id IN (
			SELECT DISTINCT department_id
			FROM employees
			WHERE last_name LIKE '%u%'
			);

# 5.查询在部门的location_id为1700的部门工作的员工的员工号
SELECT employee_id
FROM employees
WHERE department_id IN (
			SELECT department_id
			FROM departments
			WHERE location_id = 1700
			);

# 6.查询管理者是King的员工姓名和工资
# 方法一:自连接
SELECT e.`last_name`, e.`salary`
FROM employees e JOIN employees m
ON e.`manager_id` = m.`employee_id`
WHERE m.`last_name` = 'King';

# 方法二:多行子查询
SELECT last_name, salary
FROM employees
WHERE manager_id IN (
		SELECT employee_id
		FROM employees
		WHERE last_name = 'King'
		);

# 方法三:EXISTS
SELECT last_name, salary
FROM employees e1
WHERE EXISTS (
		SELECT *
		FROM employees
		WHERE e1.`manager_id` = employee_id
		AND last_name = 'King'
		);
		
# 7.查询工资最低的员工信息: last_name, salary
# 方法一:单行子查询
SELECT last_name, salary
FROM employees
WHERE salary = (
		SELECT MIN(salary)
		FROM employees
		);

# 方法二:多行子查询(使用ALL)
SELECT last_name, salary
FROM employees
WHERE salary <= ALL (
		SELECT salary
		FROM employees
		);

# 8.查询平均工资最低的部门信息
# 方法一:使用ALL
SELECT *
FROM departments
WHERE department_id = (
			SELECT department_id
			FROM employees
			GROUP BY department_id
			HAVING AVG(salary) <= ALL (
						SELECT AVG(salary)
						FROM employees
						GROUP BY department_id
						)
			);
			
# 方法二:将最内部子查询的结果集作为一张临时表
SELECT *
FROM departments
WHERE department_id = (
			SELECT department_id
			FROM employees
			GROUP BY department_id
			HAVING AVG(salary) = (
						SELECT MIN(avg_sal)
						FROM (
							SELECT AVG(salary) "avg_sal"
							FROM employees
							GROUP BY department_id
							) t_avg_sal
						)
			);
			
# 方式三:使用ORDER BY + LIMIT
SELECT *
FROM departments
WHERE department_id = (
			SELECT department_id
			FROM employees
			GROUP BY department_id
			HAVING AVG(salary) = (
						SELECT AVG(salary) "avg_sal"
						FROM employees
						GROUP BY department_id
						ORDER BY avg_sal ASC
						LIMIT 0, 1
						)
			);

# 方式四:使用多表查询的思想
SELECT d.*
FROM departments d JOIN (
			SELECT department_id, AVG(salary) "avg_sal"
			FROM employees
			GROUP BY department_id
			ORDER BY avg_sal
			LIMIT 0, 1
			) m
ON d.`department_id` = m.department_id;
	
# 9.查询平均工资最低的部门信息和该部门的平均工资(相关子查询)
# 方法一:使用ALL
SELECT d.*, (SELECT AVG(salary) FROM employees WHERE department_id = d.`department_id`) "avg_sal"
FROM departments d
WHERE department_id = (
			SELECT department_id
			FROM employees
			GROUP BY department_id
			HAVING AVG(salary) <= ALL (
						SELECT AVG(salary)
						FROM employees
						GROUP BY department_id
						)
			);
			
# 方法二:将最内部子查询的结果集作为一张临时表
SELECT d.*, (SELECT AVG(salary) FROM employees WHERE department_id = d.`department_id`) "avg_sal"
FROM departments d
WHERE department_id = (
			SELECT department_id
			FROM employees
			GROUP BY department_id
			HAVING AVG(salary) = (
						SELECT MIN(avg_sal)
						FROM (
							SELECT AVG(salary) "avg_sal"
							FROM employees
							GROUP BY department_id
							) t_avg_sal
						)
			);
			
# 方式三:使用ORDER BY + LIMIT
SELECT d.*, (SELECT AVG(salary) FROM employees WHERE department_id = d.`department_id`) "avg_sal"
FROM departments d
WHERE department_id = (
			SELECT department_id
			FROM employees
			GROUP BY department_id
			HAVING AVG(salary) = (
						SELECT AVG(salary) "avg_sal"
						FROM employees
						GROUP BY department_id
						ORDER BY avg_sal ASC
						LIMIT 0, 1
						)
			);

# 方式四:使用多表查询的思想
SELECT d.*, avg_sal
FROM departments d JOIN (
			SELECT department_id, AVG(salary) "avg_sal"
			FROM employees
			GROUP BY department_id
			ORDER BY avg_sal
			LIMIT 0, 1
			) m
ON d.`department_id` = m.department_id;

# 10.查询平均工资最高的 job 信息
# 方法一:使用ALL
SELECT *
FROM jobs
WHERE job_id = (
		SELECT job_id
		FROM employees
		GROUP BY job_id
		HAVING AVG(salary) >= ALL (
					SELECT AVG(salary)
					FROM employees
					GROUP BY job_id
					)
		);

# 方式二:将最内部子查询的结果集作为一张临时表
SELECT *
FROM jobs
WHERE job_id = (
		SELECT job_id
		FROM employees
		GROUP BY job_id
		HAVING AVG(salary) = (
					SELECT MAX(avg_sal) 
					FROM (
						SELECT AVG(salary) "avg_sal" 
						FROM employees
						GROUP BY job_id
						) t_job_avg_sal
					)
		);

# 方式三:使用ORDER BY + LIMIT
SELECT *
FROM jobs
WHERE job_id = (
		SELECT job_id
		FROM employees
		GROUP BY job_id
		HAVING AVG(salary) = (
					SELECT AVG(salary) "avg_sal"
					FROM employees
					GROUP BY job_id
					ORDER BY avg_sal DESC
					LIMIT 0, 1
					)
		);

# 方式四:使用多表查询的思想
SELECT j.*
FROM jobs j JOIN (
			SELECT job_id, AVG(salary) "avg_sal"
			FROM employees
			GROUP BY job_id
			ORDER BY avg_sal DESC
			LIMIT 0, 1
			) t_job_max_sal
ON j.`job_id` = t_job_max_sal.job_id;

# 11.查询平均工资高于公司平均工资的部门有哪些?
SELECT department_id
FROM employees
WHERE department_id IS NOT NULL
GROUP BY department_id
HAVING AVG(salary) > (
			SELECT AVG(salary)
			FROM employees
			);

# 12.查询出公司中所有 manager 的详细信息
# 方式一:自连接
SELECT DISTINCT m.*
FROM employees e JOIN employees m
ON e.`manager_id` = m.`employee_id`

# 方式二:多行子查询
SELECT *
FROM employees
WHERE employee_id IN (
			SELECT DISTINCT manager_id
			FROM employees
			)
			
# 方式三:EXISITS
SELECT *
FROM employees m
WHERE EXISTS (
		SELECT *
		FROM employees e
		WHERE m.`employee_id` = e.`manager_id`
		)

# 13.各个部门中 最高工资中最低的那个部门的 最低工资是多少?
SELECT MIN(salary)
FROM employees
GROUP BY department_id 
HAVING MAX(salary) = (
			SELECT MAX(salary) "max_sal"
			FROM employees
			GROUP BY department_id
			ORDER BY max_sal
			LIMIT 0, 1
			);

# 14.查询平均工资最高的部门的 manager 的详细信息: last_name, department_id, email, salary
SELECT last_name, department_id, email, salary
FROM employees
WHERE employee_id IN (
			SELECT DISTINCT manager_id
			FROM employees
			WHERE department_id = (
						SELECT department_id
						FROM employees
						GROUP BY department_id
						HAVING AVG(salary) = (
									SELECT MAX(avg_sal)
									FROM (
										SELECT AVG(salary) "avg_sal"
										FROM employees
										GROUP BY department_id
										) t_dept_avg_sal
									)
						)
			);

# 15. 查询部门的部门号,其中不包括job_id是"ST_CLERK"的部门号
# 方式一:NOT IN
SELECT DISTINCT department_id
FROM employees
WHERE department_id NOT IN (
			SELECT DISTINCT department_id
			FROM employees
			WHERE job_id = 'ST_CLERK'
			)
AND department_id IS NOT NULL;

# 方式二:NOT EXISTS
SELECT DISTINCT department_id
FROM employees e1
WHERE NOT EXISTS (
		SELECT *
		FROM employees e2
		WHERE job_id = 'ST_CLERK'
		AND e1.`department_id` = e2.`department_id`
		)
AND department_id IS NOT NULL;

# 16. 选择所有没有管理者的员工的last_name
# 方法一:
SELECT last_name
FROM employees
WHERE manager_id IS NULL;

# 方法二:
SELECT last_name
FROM employees e1
WHERE NOT EXISTS (
		SELECT *
		FROM employees e2
		WHERE e1.`manager_id` = e2.`employee_id`
		);

# 17.查询员工号、姓名、雇用时间、工资,其中员工的管理者为 'De Haan'
# 方式一:IN
SELECT employee_id, last_name, hire_date, salary
FROM employees
WHERE manager_id IN (
		SELECT employee_id
		FROM employees
		WHERE last_name = 'De Haan'
		);

# 方式二:EXISTS
SELECT employee_id, last_name, hire_date, salary
FROM employees e1
WHERE EXISTS (
	SELECT *
	FROM employees e2
	WHERE last_name = 'De Haan'
	AND e1.`manager_id` = e2.`employee_id`
	);
		
# 18.查询各部门中工资比本部门平均工资高的员工的员工号, 姓名和工资(相关子查询)
SELECT employee_id, last_name, salary
FROM employees e1
WHERE salary > (
		SELECT AVG(salary)
		FROM employees
		WHERE department_id = e1.`department_id`
		)

# 19.查询每个部门下的部门人数大于 5 的部门名称(相关子查询)
# 方式一:
SELECT department_name
FROM departments
WHERE department_id IN (
			SELECT department_id
			FROM employees
			GROUP BY department_id
			HAVING COUNT(*) > 5
			);


# 方式二:相关子查询
SELECT department_name
FROM departments d
WHERE 5 < (
		SELECT COUNT(*)
		FROM employees
		WHERE department_id = d.`department_id`
		);

# 20.查询每个国家下的部门个数大于 2 的国家编号(相关子查询)
SELECT country_id
FROM locations l
WHERE 2 < (
	SELECT COUNT(*)
	FROM departments
	WHERE location_id = l.`location_id`
	);

mployee_id
FROM employees
WHERE last_name = ‘De Haan’
);

方式二:EXISTS

SELECT employee_id, last_name, hire_date, salary
FROM employees e1
WHERE EXISTS (
SELECT *
FROM employees e2
WHERE last_name = ‘De Haan’
AND e1.manager_id = e2.employee_id
);

18.查询各部门中工资比本部门平均工资高的员工的员工号, 姓名和工资(相关子查询)

SELECT employee_id, last_name, salary
FROM employees e1
WHERE salary > (
SELECT AVG(salary)
FROM employees
WHERE department_id = e1.department_id
)

19.查询每个部门下的部门人数大于 5 的部门名称(相关子查询)

方式一:

SELECT department_name
FROM departments
WHERE department_id IN (
SELECT department_id
FROM employees
GROUP BY department_id
HAVING COUNT(*) > 5
);

方式二:相关子查询

SELECT department_name
FROM departments d
WHERE 5 < (
SELECT COUNT(*)
FROM employees
WHERE department_id = d.department_id
);

20.查询每个国家下的部门个数大于 2 的国家编号(相关子查询)

SELECT country_id
FROM locations l
WHERE 2 < (
SELECT COUNT(*)
FROM departments
WHERE location_id = l.location_id
);


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值