第九章:子查询
9.1:子查询的基本使用
-
子查询的基本语法结构
SELECT .... FROM .... WHERE expr operator (SELECT ... FROM ... WHERE ...);
- 子查询(内查询)在主查询之前一次执行完成。
- 子查询的结果被主查询(外查询)使用。
- 注意事项
- 子查询要包含在括号内。
- 将子查询放在比较条件的右侧。
- 单行操作符对应单行子查询,多行操作符对应多行子查询
-
子查询的分类
-
分类方式1
我们按内查询的结果返回一条还是多条记录,将子查询分为单行子查询、多行子查询。
-
分类方式2
我们按内查询是否被执行多次,将子查询划分为相关(或关联)子查询和不相关(或非关联)子查询。
子查询从数据表中查询了数据结果,如果这个数据结果只执行一次,然后这个数据结果作为主查询的条件进行执行,那么这样的子查询叫做不相关子查询。
同样,如果子查询需要执行多次,即采用循环的方式,先从外部查询开始,每次都传入子查询进行查询,然后再将结果反馈给外部,这种嵌套的执行方式就称为相关子查询。
-
9.2:单行子查询
-
单行比较操作符
操作符 含义 =
equal to
>
greater than
>=
greater than or equal to
<
less than
<=
less than or equal to
<>
not equal to
-
代码示例
-
返回
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 );
-
-
HAVING
中的子查询首先执行子查询,向主查询中的
HAVING
子句返回结果。-
查询最低工资大于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 );
-
-
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;
-
-
子查询中的空值问题
SELECT last_name, job_id FROM employees WHERE job_id = ( # 子查询为NULL SELECT job_id FROM employees WHERE last_name = 'Haas' );
-
非法使用子查询语句
# 错误代码:1242 # Subquery returns more than 1 row SELECT employee_id, last_name FROM employees WHERE salary = ( SELECT MIN(salary) FROM employees GROUP BY department_id );
9.3:多行子查询
-
多行比较操作符
操作符 含义 IN
等于列表中的任意一个 ANY
需要和单行比较操作符一起使用,和子查询返回的某一个值比较 ALL
需要和单行比较操作符一起使用,和子查询返回的所有值比较 SOME
实际上是 ANY
的别名,作用相同,一般常使用ANY
-
实例代码
-
返回其他
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
。# 方式一 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 );
-
-
空值问题
-
一行数据都没有
SELECT last_name FROM employees WHERE employee_id NOT IN( SELECT manager_id FROM employees );
-
9.4:相关子查询
-
相关子查询执行流程
如果子查询的执行依赖于外部查询,通常情况下都是因为子查询中的表用到了外部的表,并进行了条件关联,因此每执行一次外部查询,子查询都要重新计算一次,这样的子查询就称之为关联子查询。
相关子查询按照一行接一行的顺序执行,主查询的每一行都执行一次子查询。
-
代码示例
-
查询员工中工资大于本部门平均工资的员工的
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
排序。# 在ORDER BY中使用子查询 SELECT employee_id, salary FROM employees e ORDER BY ( SELECT department_name FROM departments d WHERE e.`department_id` = d.`department_id` ) ASC;
-
-
EXISTS
与NOT EXISTS
关键字关联子查询通常也会和
EXISTS
操作符一起来使用,用来检查在子查询中是否存在满足条件的行。- 如果在子查询中不存在满足条件的行:条件返回
FALSE
、继续在子查询中查找 - 如果在子查询中满足条件的行:不在子查询中继续查找、条件返回
TRUE
NOT EXISTS
关键字表示如果不存在某种条件,则返回TRUE
,否则返回FALSE
。
查询公司管理者的
employee_id
、last_name
、job_id
、department_id
信息。#方式1:自连接 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`; #方式2:子查询 SELECT employee_id, last_name, job_id, department_id FROM employees WHERE employee_id IN ( SELECT DISTINCT manager_id FROM employees ); #方式3:使用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
。#方式1: 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; #方式2: SELECT department_id, department_name FROM departments d WHERE NOT EXISTS ( SELECT * FROM employees e WHERE d.`department_id` = e.`department_id` );
- 如果在子查询中不存在满足条件的行:条件返回
-
一个思考题
-
自连接和子查询两种方式有好坏之分吗?
自连接方式好。
题目中可以使用子查询,也可以使用自连接。一般情况建议你使用自连接,因为在许多
BDMS
的处理过程中,对于自连接的处理速度要比子查询快得多。 可以这样理解:子查询实际上是通过未知表进行查询后的条件判断,而自连接是通过已知的自身数据表进行条件判断,因此在大部分
DBMS
中都对自连接处理进行了优化。
-