在一个查询结果中嵌入的另一个查询
需求:谁的工资比Abel高?
方式1:
SELECT salaryFROM employeesWHERE last_name = 'Abel'SELECT last_name,salaryFROM employeesWHERE salary > 11000;
方式2:自连接
SELECT e2.last_name,e2.salaryFROM employees e1 INNER JOIN employees e2ON e1.employee_id = e2.employee_idWHERE e2.salary > e1.salary AND e1.last_name = 'Abel';
方式3:子查询
SELECT last_name,salaryFROM employeesWHERE salary > (SELECT salaryFROM employeesWHERE last_name = 'Abel');
子查询的使用
称谓的规范:外查询(主查询)、内查询(子查询)
子查询在主查询之前一次执行完成
- 子查询的结果被主查询使用
- 子查询要包含在括号内
- 将子查询放在比较条件的右侧
- 单行操作符对应单行子查询,多行操作符对应多行子查询
子查询的分类
角度1:单行子查询 vs 多行子查询
- 从内查询返回的结果的条目数
角度2:相关子查询 vs 非相关子查询
- 内查询是否被执行多次
- 比如:相关子查询的需求:查询工资大于本部门平均工资的员工信息
- 不相关子查询的需求:查询工资大于本公司平均工资的员工信息
单行子查询
- 子查询编写技巧(或步骤)
- 从里往外写
- 从外往里写
子查询
查询工资大于149号员工工资的员工的信息
SELECT employee_id,employee_name,salaryFROM employeesWHERE salary > (SELECT salaryFROM employeesWHERE employee_id = 149)
返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资
SELECT employee_name,job_id,salaryFROM employeesWHERE salary > (SELECT salaryFROM employeesWHERE employee_id = 143) ANDjob_id = (SELECT job_idFROM employeesWHERE job_id = 141)
返回公司工资最少的员工的last_name,job_id和salary
SELECT last_name,job_id,salaryFROM employeesWHERE salary = (SELECT MIN(salary)FROM employees)
查询与141号员工的manager_id和department_id相同的其他员工的employee_id,manager_id,department_id
SELECT employee_id,manager_id,department_idFROM employeesWHERE manager_id = (SELECT manager_idFROM employeesWHERE employee_id = 141)AND department_id = (SELECT department_idFROM employeesWHERE department_id = 141)AND employee_id <> 141;
HAVING中的子查询
查询最低工资大于50号部门最低工资的部门id和其最低工资
SELECT department_id,MIN(salary)FROM employeesWHERE department_id IS NOT NULLGROUP BY department_idHAVING MIN(salary) > (SELECT MIN(salary)FROM employeesWHERE deparment_id = 50)
CASE中的子查询
显式员工的employee_id,last_name和location。其中,若员工department_id与location_id为1800的department_id相同,则location为”Canada”,其余为’USA’
SELECT employee_id,last_name,(CASE department_idWHEN(SELECT department_id FROM departmentsWHERE location_id = 1800)THEN 'Canada' ELSE 'USA' END) "location"FROM employeesSELECT department_idFROM departmentsWHERE location_id = 1800
子查询中的空值问题
SELECT last_name,job_idFROM employeesWHERE job_id =(SELECT job_idFROM employeesWHERE last_name = 'Haas' //没有haas这个人);
- 若是空值,则子查询不返回任何行
非法使用子查询
- 应该使用多行子查询操作符(IN)
SELECT employee_id,last_nameFROM employeesWHERE salary = ( //要求的是单行数据SELECT MIN(salary) //里面有多行数据FROM employeesGROUP BY department_id);
多行子查询
- 集合标记子查询
- 内查询返回多行
- 使用多行比较操作符
多行比较操作符
- IN:等于列表中的任意一个
- ANY:和子查询返回的某一个值比较
- ALL:和子查询返回的所有值比较
SOME:是ANY的别名,作用相同
IN
SELECT employee_id,last_nameFROM employeesWHERE salary IN (SELECT MIN(salary)FROM employeesGROUP BY department_id;)
ANY / ALL
返回其他job_id中比job_id为’IT_PROG’部门任一工资低的员工的员工号、姓名、job_id及salary
SELECT employee_id,last_name,job_id,salaryFROM employeesWHERE job_id <> 'IT_PROG'AND salary < ANY(SELECT salaryFROM employeesWHERE job_id = 'IT_PROG');SELECT salaryFROM employeesWHERE job_id = 'IT_PROG'
返回其他job_id中比job_id为’IT_PROG’部门所有工资低的员工的员工号、姓名、job_id及salary
SELECT employee_id,last_name,job_id,salaryFROM employeesWHERE job_id <> 'IT_PROG'AND salary < ALL(SELECT salaryFROM employeesWHERE job_id = 'IT_PROG');
查询平均工资最低的部门id
- MySQL中聚合函数是不能嵌套使用的
方式1:
SELECT MIN(AVG(salary)) //错误的FROM employeesGROUP BY department_id
SELECT MIN(avg_sal)FROM (SELECT AVG(salary) avg_salFROM employeesGROUP BY department_id) t_dept_avg_sal
SELECT department_idFROM employeesGROUP BY department_idHAVING AVG(salary) = (SELECT MIN(avg_sal)FROM (SELECT AVG(salary) avg_salFROM employeesGROUP BY department_id) t_dept_avg_sal);
方式2:
SELECT department_idFROM employeesGROUP BY department_idHAVING AVG(salary) <= ALL(SELECT AVG(salary) avg_salFROM employeesGROUP BY department_id));
- 空值问题
查询不是管理者的其他人的名字
SELECT last_nameFROM employeesWHERE employee_id NOT IN (SELECT manager_id //内查询里有Null值FROM employeesWHERE manager_id IS NOT NULL);
相关子查询
- 如果子查询的执行依赖外部查询,是因为子查询中的表用到了外部的表,进行了条件关联,每次执行一次外部查询,子查询都要重新计算一次。
查询员工中种子大于本公司平均工资的员工的last_name,salary和其department_id
SELECT last_name,salary,department_idFROM employeesWHERE salary > (SELECT AVG(salary)FROM employees)
- 查询员工中种子大于本部门平均工资的员工的last_name,salary和其department_id
方式1:使用相关子查询
SELECT last_name,salary,department_idFROM employees e1WHERE salary > (SELECT AVG(salary)FROM employees e2WHERE department_id = e1.department_id)
方式2:在FROM中声明子查询
SELECT e.last_name,e.salary,e.department_idFROM employees e,(select department_id,AVG(salary) avg_salfrom employeesGROUP BY department_id) t_dept_avg_salWHERE e.department_id = t_dept_avg_sal.department_idAND e.salary > t_dept_avg_sal.avg_sal
查询员工的id,salary,按照department_name排序
SELECT employee_id,salaryFROM employees eORDER BY (SELECT department_nameFROM deprtments dWHERE 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 employee_id,last_name,job_idFROM employees eWHERE 2 <= (SELECT COUNT(*)FROM job_history jWHERE e.employee_id = j.employee_id)
EXISTS 与 NOT EXISTS关键字
查询公司管理者的employee_id,last_name,job_id,department_id信息
方式1:自连接
SELECT DISTINCT mgr.employee_id,mgr.last_name,mgr.job_id,mgr.department_idFROM employees emp LEFT JOIN employees mgrON emp.manager_id = mgr.employee_id;
方式2:子查询
SELECT employee_id,last_name,job_id,department_idFROM employeesWHERE employee_id IN (SELECT DISTINCT manager_idFROM employees)
方式3:使用EXISTS
SELECT employee_id,last_name,job_id,department_idFROM employees elWHERE EXISTS(SELECT *FROM employees e2WHERE e1.employee_id = e2.manager_id)
- NOT EXISTS
查询departments表中不存在于employees表中的部门的department_id和department_name
方式1:
SELECT d.department_id,d.department_nameFROM employees e RIGHT JOIN departments dON e.department_id = d.department_idWHERE e.department_id IS NULL;
方式2:
SELECT department_id,department_nameFROM departments dWHERE NOT EXISTS (SELECT *FROM employees eWHERE d.department_id = d.department_id)
相关删除
- 使用子查询依据一个表中的数据删除另一个表的数据
删除表employees中,其与emp_history表皆有的数据
DELETE FROM employees eWHERE employee_id IN (SELECT employee_idFROM emp_history e1WHERE e1.employee_id = e.employee_id)
思考题
- 谁的工资比Abel高?
方式1:自连接
SELECT e2.last_name,e2.salaryFROM employees e1,employees e2WHERE e1.last_name = 'Abel'AND e1.salary < e2.salary
方式2:子查询
SELECT last_name,salaryFROM employeesWHERE salary > (SELECT salaryFROM employeesWHERE last_name = 'Abel')
- 自连接的方式好,
- 子查询实际上是通过未知表进行查询后的条件判断,而自连接是通过已知的自身数据表进行条件判断,因此在大部分DBMS中都对自连接处理进行了优化
课后练习
查询和Zlotkey相同部门的员工姓名和工资
SELECT e2.last_name,e2.salaryFROM employees e1,employees e2WHERE e1.department_id = e2.department_idAND e1.last_name = 'Zlotkey'
SELECT last_name,salaryFROM employeesWHERE department_id IN (SELECT department_idFROM employeesWHERE last_name = 'Zlotkey');
查询工资比公司平均工资高的员工的员工号,姓名和工资
SELECT employee_id,last_name,salaryFROM employeesWHERE salary > (SELECT AVG(salary)FROM employees);
选择工资大于所有JOB_ID = ‘SA_MAN’的员工的工资的员工的last_name,job_id,salary
SELECT last_name,job_id,salaryFROM employeeWHERE salary > ALL(SELECT salaryFROM employeesWHERE job_id = 'SA_MAN');
查询和姓名中包含字母u的员工在相同部门的员工的员工号和姓名 ```sql SELECT DISTINCT e2.employee_id,e2.last_name
FROM employees e1,employees e2 WHERE e1.last_name LIKE ‘%u%’ AND e1.department_id = e2.department_id
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的部门工作的员工的员工号```sqlSELECT department_id //从内向外FROM departmentsWHERE location_id = 1700SELECT employee_idFROM employeesWHERE department_id IN (SELECT department_idFROM departmentsWHERE location_id = 1700);
- 查询管理者是King的员工姓名和工资 ```sql SELECT employee_id FROM employees WHERE last_name = ‘King’
SELECT last_name,salary FROM employees WHERE manager_id IN ( SELECT employee_id FROM employees WHERE last_name = ‘King’ )
7. 查询工资最低的员工信息:last_name,salary```sqlSELECT last_name,salaryFROM employeesWHERE salary = (SELECT MIN(salary)FROM employees)
- 查询平均工资最低的部门信息
方式1:
SELECT MIN(avg_sal) //查询各部门的平均工资FROM (SELECT AVG(salary) avg_salFROM employeesGROUP BY department_id)t_dept_avg_salSELECT department_idFROM employeesGROUP BY department_idHAVING AVG(salary) = (SELECT MIN(avg_sal)FROM (SELECT AVG(salary) avg_salFROM employeesGROUP BY department_id)t_dept_avg_sal)SELECT *FROM departmentsWHERE department_id = (SELECT department_idFROM employeesGROUP BY department_idHAVING AVG(salary) = (SELECT MIN(avg_sal)FROM (SELECT AVG(salary) avg_salFROM employeesGROUP BY department_id)t_dept_avg_sal))
方式2:
SELECT *FROM departmentsWHERE department_id = (SELECT department_idFROM employeesGROUP BY department_idHAVING AVG(salary) <= ALL(SELECT AVG(salary)FROM employeesGROUP BY department_id))
方式3:LIMIT
SELECT AVG(salary) avg_salFROM employeesGROUP BY department_idORDER BY avg_sal ASCLIMIT 1SELECT *FROM departmentsWHERE department_id = (SELECT department_idFROM employeesGROUP BY department_idHAVING AVG(salary) = (SELECT AVG(salary) avg_salFROM employeesGROUP BY department_idORDER BY avg_sal ASCLIMIT 1));
方式4:
SELECT department_id,AVG(salary) avg_salFROM employeesGROUP BY department_idORDER BY avg_sal ASCLIMIT 0,1SELECT *FROM departments,(SELECT department_id,AVG(salary) avg_salFROM employeesGROUP BY department_idORDER BY avg_sal ASCLIMIT 0,1)t_dept_avg_salWHERE d.department_id = t_dept_avg_sal.department_id
查询平均工资最低的部门信息和该部门的平均工资(相关子查询)
SELECT d.*,(SELECT AVG(salary) FROM employees WHERE department_id = d.department_id) avg_salFROM departments dWHERE department_id = (SELECT department_idFROM employeesGROUP BY department_idHAVING AVG(salary) = (SELECT MIN(avg_sal)FROM (SELECT AVG(salary) avg_salFROM employeesGROUP BY department_id)t_dept_avg_sal))
查询平均工资最高的job信息 ```sql 与第8题相似,也有四种写法
方式4: SELECT j.* FROM jobs j,( SELECT job_id,AVG(salary) avg_sal FROM employees GROUP BY job_id ORDER BY avg_sal DESC LIMIT 0,1 ) t_job_avg_sal WHERE j.job_id = t_job_avg_sal.job_id
11. 查询平均工资高于公司平均工资的部门有那些?```sqlSELECT AVG(salary)FROM employeesSELECT department_idFROM employeesWHERE department_id IS NOT NULLGROUP BY department_idHAVING AVG(salary) > (SELECT AVG(salary)FROM employees)
查询出公司所有manager的详细信息
SELECT DISTINCT mgr.*FROM employees emp JOIN employees mgrON emp.manager_id = mgr.employee_id
各个部门中 最高工资中最低的那个部门的 最低工资是多少? ```sql SELECT MIN(salary) FROM employees WHERE department_id = (
SELECT department_idFROM employeesORDER BY department_idHAVING MAX(salary) = (SELECT MIN(max_sal)FROM (SELECT MAX(salary) max_salFROM employeesGROUP BY department_id) t_dept_max_sal)
)
方式4: SELECT MIN(salary) FROM employees e,( SELECT department_id,MAX(salary) max_sal FROM employees GROUP BY department_id ORDER BY max_sal ASC LIMIT 0,1 ) t_dept_max_sal WHERE e.department_id = t_dept_max_sal.department_id
14. 查询平均工资最高的的部门的manager的详细信息```sqlSELECT MAX(avg_sal)FROM (SELECT AVG(salary)FROM employeesGROUP BY department_id)SELECT department_idFROM employeesGROUP BY department_idHAVING AVG(salary) = (SELECT MAX(avg_sal)FROM (SELECT AVG(salary)FROM employeesGROUP BY department_id))SELECT DISTINCT manager_idFROM employeesWHERE department_id = (SELECT department_idFROM employeesGROUP BY department_idHAVING AVG(salary) = (SELECT MAX(avg_sal)FROM (SELECT AVG(salary)FROM employeesGROUP BY department_id)))
- 查询部门的部门号,其中不包括job_id是”ST_CLERK”的部门号
方式1:
SELECT department_idFROM employeesWHERE department_id NOT IN (SELECT DISTINCT department_idFROM employeesWHERE job_id = 'ST_CLERK')
方式2:
SELECT department_idFROM departments dWHERE NOT EXISTS (SELECT *FROM employees dWHERE d.department_id = e.department_idWHERE e.job_id = 'ST_CLERK')
选择所有没有管理者的员工的last_name
SELECT last_nameFROM employees empWHERE NOT EXISTS (select *from employees mgrwhere emp.manager_id = mgr.employee_id)
查询员工号、姓名、雇佣时间、工资。其中员工的管理者为’De Haan’
SELECT employee_id,last_name,hire_date,salaryFROM employeesWHERE manager_id IN (SELECT employee_idFROM employeesWHERE last_name = 'De Haan')
查询各部门中工资比本部门平均工资高的员工的员工号,姓名和工资
SELECT department_id,last_name,salaryFROM employees empWHERE salary > (SELECT AVG(salary)FROM employeesWHERE department_id = emp.department_id)
查询各个部门下的部门人数大于5的部门名称
SELECT department_nameFROM departments dWHERE 5 < (SELECT COUNT(*)from employees eWHERE d.department_id = e.department_id)
查询每个国家下的部门个数大于2的国家编号
SELECT country_idFROM location_idWHERE 2 < (SELECT count(*)FROM departments dWHERE l.location_id = d.loaction_id)
编写技巧
- 如果子查询相对比较简单,建议从外往里写。一旦子查询结构比较复杂,建议从里往外写
- 如果是相关子查询,通常都是从外往里写
