#第九章、子查询 #1、由一个具体的需求,引入子查询 #需求:谁的工资比Abel的高? #方式一: SELECT last_name,salary FROM employees WHERE last_name = 'Abel'; SELECT last_name,salary FROM employees WHERE salary > 11000; #方式二:自连接 SELECT e2.last_name,e2.salary FROM employees e1,employees e2 WHERE e2.salary > e1.salary #多表的连接条件 AND e1.last_name = 'Abel'; #方式三:子查询 SELECT last_name,salary FROM employees WHERE salary > ( SELECT salary FROM employees WHERE last_name = 'Abel' ); #2、称谓的规范:外查询(或主查询)、内查询(或子查询) /* 子查询(内查询)在主查询之前一次执行完成。 子查询的结果被主查询(外查询)使用。 注意事项: 子查询要包含在括号内 将子查询放在比较条件的右侧 单行操作符对应单行子查询,多行操作符对应多行子查询 */ /* 3、子查询的分类 角度1:从内查询返回的结果的条目数 单行子查询 vs 多行子查询 角度2:内查询是否被执行多次 相关子查询 VS 不相关子查询 比如:相关子查询的需求:查询工资大于本部门平均工资的员工信息 不相关子查询的需求:查询工资大于本公司平均工资的员工信息 */ #子查询的编写技巧(或步骤):(1)、从里往外写 (2)、从外往里写 #4、单行子查询 #4.1、单行操作符: = != > >= < <= #题目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; #题目5:查询最低工资大于110号部门最低工资的部门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 = 110 ); #题目6:显示员工的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; #4.2、子查询中的空值问题 SELECT last_name,job_id FROM employees WHERE job_id = ( SELECT job_id FROM employees WHERE last_name = 'Haas' ); #4.3、非法使用子查询 #错误:Subquery returns more than 1 row -- SELECT employee_id,last_name -- FROM employees -- WHERE salary = ( -- SELECT MIN(salary) -- FROM employees -- GROUP BY department_id -- ); #5、多行子查询 #5.1、多行子查询的操作符:IN ANY ALL SOME(同ANY) #5.2、举例 # IN SELECT employee_id,last_name FROM employees WHERE salary IN ( SELECT MIN(salary) FROM employees GROUP BY department_id ); # ANY / ALL #题目:返回其他job_id中比job_id为IT_PROG部门任一工资低的员工的员工号、姓名、 #job_id以及salary SELECT employee_id,last_name,job_id,salary FROM employees WHERE job_id <> 'IT_PROG' AND salary < ANY( SELECT salary FROM employees WHERE job_id = 'IT_PROG' ); #题目:返回其他job_id中比job_id为IT_PROG部门所有工资低的员工的员工号、姓名、 #job_id以及salary SELECT employee_id,last_name,job_id,salary FROM employees WHERE job_id <> 'IT_PROG' AND salary < ALL ( SELECT salary FROM employees WHERE job_id = 'IT_PROG' ); #题目:查询平均工资最低的部门id #MySQL中聚合函数是不能嵌套的 #方式一: 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_dept_avg_sal ); #方式二: SELECT department_id FROM employees GROUP BY department_id HAVING AVG(salary) <= ALL ( SELECT AVG(salary) avg_sal FROM employees GROUP BY department_id ); #5.3、空值问题 SELECT last_name FROM employees WHERE employee_id NOT IN ( SELECT manager_id FROM employees WHERE manager_id IS NOT NULL ); #6、相关子查询 #6.1 #回顾:查询员工中工资大于公司平均工资的员工的last_name,salary和其department_id SELECT last_name,salary,department_id FROM employees WHERE salary > ( SELECT AVG(salary) FROM employees ); #题目2:查询员工中工资大于本部门平均工资的员工的last_name,salary和其department_id #方式一:使用相关子查询 SELECT last_name,salary,department_id FROM employees e1 WHERE salary > ( 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) 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.avg_sal; #题目:查询员工的id,salary,按照department_name排序 SELECT employee_id,salary FROM employees e ORDER BY ( SELECT department_name FROM departments d WHERE e.department_id = d.department_id ) ASC; #结论:在SELECT中,除了GROUP BY和LIMIT之外,其他位置都可以声明子查询。 #题目:若employees表中employee_id与job_history表中employee_id相同的数目 #不小于2,输出这些相同id的员工的employee_id,last_name和job_id。 SELECT * FROM job_history; SELECT employee_id,last_name,job_id FROM employees e WHERE 2 <=( SELECT COUNT(*) FROM job_history j WHERE e.employee_id = j.employee_id ); #6.2、EXISTS与NOT EXISTS关键字 #题目:查询公司管理者的employee_id,last_name,job_id,department_id信息 #方式一:自连接 SELECT DISTINCT mgr.employee_id,mgr.last_name,mgr.job_id,mgr.department_id FROM employees emp JOIN employees mgr ON emp.manager_id = mgr.employee_id; #方式二:子查询 SELECT DISTINCT employee_id,last_name,job_id,department_id FROM employees emp WHERE employee_id IN ( SELECT DISTINCT manager_id FROM employees ); #我做的 SELECT employee_id,last_name,job_id,department_id FROM employees WHERE employee_id IN ( SELECT manager_id FROM employees WHERE manager_id IS NOT NULL GROUP BY manager_id ); #方式三:EXISTS SELECT employee_id,last_name,job_id,department_id FROM employees e1 WHERE EXISTS ( SELECT * FROM employees e2 WHERE e1.employee_id = e2.manager_id ); #题目:查询departments表中,不存在与employees表中的部门的department_id和department_name。 #方式一:外连接 SELECT d.department_id,d.department_name FROM employees e RIGHT JOIN departments d ON e.department_id = d.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 );