十一子查询详解
Posted Amo Xiang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了十一子查询详解相关的知识,希望对你有一定的参考价值。
文章目录
前置知识:
一、数据库开发与实战专栏导学及数据库基础概念入门
二、MySQL 介绍及 MySQL 安装与配置
三、MySQL 数据库的基本操作
四、MySQL 存储引擎及数据类型
五、数据导入与基本的 SELECT 语句
六、MySQL 数据库练习题1(包含前5章练习题目及答案)
七、MySQL 多表查询详解(附练习题及答案----超详细)
八、MySQL 常用函数汇总(1)
九、MySQL 常用函数汇总(2)
十、MySQL 聚合函数、分组查询及过滤分组
子查询指一个查询语句嵌套在另一个查询语句内部的查询,这个特性从 MySQL 4.1 开始引入。在 SELECT
子句中先计算子查询,子查询结果作为外层另一个查询的过滤条件,查询可以基于一个表或者多个表。子查询中常用的操作符有 ANY(SOME)、ALL、IN、EXISTS
。 子查询可以添加到 SELECT、UPDATE 和 DELETE
语句中,而且可以进行多层嵌套。子查询中也可以使用比较运算符,如 <、<=、>、>=、!=
等。本文将介绍如何在 SELECT
语句中嵌套子查询。
一、需求分析与问题解决
1.1 实际问题
问题引入: 查询 employees 表中谁的工资比 Abel 高?
解决方案:
# 方案1:
mysql> SELECT salary FROM employees WHERE last_name='Abel';
+----------+
| salary |
+----------+
| 11000.00 |
+----------+
1 row in set (0.00 sec)
mysql> SELECT last_name,job_id,salary FROM employees WHERE salary>11000;
+-----------+---------+----------+
| last_name | job_id | salary |
+-----------+---------+----------+
| King | AD_PRES | 24000.00 |
| Kochhar | AD_VP | 17000.00 |
| De Haan | AD_VP | 17000.00 |
| Greenberg | FI_MGR | 12000.00 |
| Russell | SA_MAN | 14000.00 |
| Partners | SA_MAN | 13500.00 |
| Errazuriz | SA_MAN | 12000.00 |
| Ozer | SA_REP | 11500.00 |
| Hartstein | MK_MAN | 13000.00 |
| Higgins | AC_MGR | 12000.00 |
+-----------+---------+----------+
10 rows in set (0.00 sec)
#方案2
mysql> SELECT e1.last_name,e1.job_id,e1.salary FROM employees e1 INNER JOIN employees e2 ON e2.last_name='Abel'
AND e1.salary>e2.salary;
+-----------+---------+----------+
| last_name | job_id | salary |
+-----------+---------+----------+
| King | AD_PRES | 24000.00 |
| Kochhar | AD_VP | 17000.00 |
| De Haan | AD_VP | 17000.00 |
| Greenberg | FI_MGR | 12000.00 |
| Russell | SA_MAN | 14000.00 |
| Partners | SA_MAN | 13500.00 |
| Errazuriz | SA_MAN | 12000.00 |
| Ozer | SA_REP | 11500.00 |
| Hartstein | MK_MAN | 13000.00 |
| Higgins | AC_MGR | 12000.00 |
+-----------+---------+----------+
10 rows in set (0.01 sec)
#方案3
mysql> SELECT last_name,job_id,salary FROM employees
-> WHERE salary > (SELECT salary FROM employees WHERE last_name='Abel');
+-----------+---------+----------+
| last_name | job_id | salary |
+-----------+---------+----------+
| King | AD_PRES | 24000.00 |
| Kochhar | AD_VP | 17000.00 |
| De Haan | AD_VP | 17000.00 |
| Greenberg | FI_MGR | 12000.00 |
| Russell | SA_MAN | 14000.00 |
| Partners | SA_MAN | 13500.00 |
| Errazuriz | SA_MAN | 12000.00 |
| Ozer | SA_REP | 11500.00 |
| Hartstein | MK_MAN | 13000.00 |
| Higgins | AC_MGR | 12000.00 |
+-----------+---------+----------+
10 rows in set (0.00 sec)
1.2 子查询的基本使用
子查询的基本语法结构:
子查询(内查询) 在主查询之前一次执行完成。子查询的结果被 主查询(外查询) 使用。注意事项:
子查询要包含在括号内
将子查询放在比较条件的右侧
单行操作符对应单行子查询,多行操作符对应多行子查询
1.3 子查询的分类
分类方式1: 我们按内查询的结果返回一条还是多条记录,将子查询分为 单行子查询 、 多行子查询
。 单行子查询如下图所示:
多行子查询如下图所示:
我们按内查询是否被执行多次,将子查询划分为 相关(或关联)子查询
和 不相关(或非关联)子查询
子查询从数据表中查询了数据结果,如果这个数据结果只执行一次,然后这个数据结果作为主查询的条件进行执行,那么这样的子查询叫做 不相关子查询。 同样,如果子查询需要执行多次,即采用循环的方式,先从外部查询开始,每次都传入子查询进行查询,然后再将结果反馈给外部,这种嵌套的执行方式就称为 相关子查询。
二、单行子查询
单行比较操作符: =,>,>=,<,<=,<>
【练习1】查询工资大于149号员工工资的员工的信息。
#查询工资大于149号员工工资的员工的信息
mysql> SELECT last_name,salary,job_id FROM employees
-> WHERE salary > (
-> SELECT salary FROM employees WHERE employee_id=149);
+-----------+----------+---------+
| last_name | salary | job_id |
+-----------+----------+---------+
| King | 24000.00 | AD_PRES |
| Kochhar | 17000.00 | AD_VP |
| De Haan | 17000.00 | AD_VP |
| Greenberg | 12000.00 | FI_MGR |
| Raphaely | 11000.00 | PU_MAN |
| Russell | 14000.00 | SA_MAN |
| Partners | 13500.00 | SA_MAN |
| Errazuriz | 12000.00 | SA_MAN |
| Cambrault | 11000.00 | SA_MAN |
| Ozer | 11500.00 | SA_REP |
| Abel | 11000.00 | SA_REP |
| Hartstein | 13000.00 | MK_MAN |
| Higgins | 12000.00 | AC_MGR |
+-----------+----------+---------+
13 rows in set (0.00 sec)
分析如下图所示:
【练习2】返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资
mysql> #返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资
mysql> 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 |
+-------------+----------+---------+
| Nayer | ST_CLERK | 3200.00 |
| Mikkilineni | ST_CLERK | 2700.00 |
| Bissot | ST_CLERK | 3300.00 |
| Atkinson | ST_CLERK | 2800.00 |
| Mallin | ST_CLERK | 3300.00 |
| Rogers | ST_CLERK | 2900.00 |
| Ladwig | ST_CLERK | 3600.00 |
| Stiles | ST_CLERK | 3200.00 |
| Seo | ST_CLERK | 2700.00 |
| Rajs | ST_CLERK | 3500.00 |
| Davies | ST_CLERK | 3100.00 |
+-------------+----------+---------+
11 rows in set (0.00 sec)
【练习3】返回公司工资最少的员工的last_name,job_id和salary
mysql> #返回公司工资最少的员工的last_name,job_id和salary
mysql> SELECT last_name,job_id,salary
-> FROM employees WHERE salary = (
-> SELECT MIN(salary) FROM employees);
+-----------+----------+---------+
| last_name | job_id | salary |
+-----------+----------+---------+
| Olson | ST_CLERK | 2100.00 |
+-----------+----------+---------+
1 row in set (0.00 sec)
【练习4】查询与141号员工的manager_id和department_id相同的其他员工的employee_id,manager_id,department_id。
mysql> #查询与141号员工的manager_id和department_id相同的其他员工的employee_id,
mysql> #manager_id,department_id
mysql> #方案1
mysql> 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;
+-------------+------------+---------------+
| employee_id | manager_id | department_id |
+-------------+------------+---------------+
| 142 | 124 | 50 |
| 143 | 124 | 50 |
| 144 | 124 | 50 |
| 196 | 124 | 50 |
| 197 | 124 | 50 |
| 198 | 124 | 50 |
| 199 | 124 | 50 |
+-------------+------------+---------------+
7 rows in set (0.00 sec)
mysql> #方案2
mysql> 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;
+-------------+------------+---------------+
| employee_id | manager_id | department_id |
+-------------+------------+---------------+
| 142 | 124 | 50 |
| 143 | 124 | 50 |
| 144 | 124 | 50 |
| 196 | 124 | 50 |
| 197 | 124 | 50 |
| 198 | 124 | 50 |
| 199 | 124 | 50 |
+-------------+------------+---------------+
7 rows in set (0.00 sec)
【练习5】查询最低工资大于40号部门最低工资的部门id和其最低工资。
mysql> #查询最低工资大于40号部门最低工资的部门id和其最低工资。
mysql> 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=40);
+---------------+-------------+
| department_id | MIN(salary) |
+---------------+-------------+
| NULL | 7000.00 |
| 70 | 10000.00 |
| 90 | 17000.00 |
| 100 | 6900.00 |
| 110 | 8300.00 |
+---------------+-------------+
5 rows in set (0.00 sec)
【练习6】显式员工的employee_id,last_name和location。其中,若员工department_id与location_id为1800 的department_id相同,则location为’Canada’,其余则为’USA’。
mysql> 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 LIMIT 5;
+-------------+-----------+----------+
| employee_id | last_name | location |
+-------------+-----------+----------+
| 100 | King | USA |
| 101 | Kochhar | USA |
| 102 | De Haan | USA |
| 103 | Hunold | USA |
| 104 | Ernst | USA |
+-------------+-----------+----------+
5 rows in set (0.00 sec)
【练习7】子查询的空值问题。
SELECT last_name, job_id FROM employees
WHERE job_id =
#没有姓名为Haas的员工 为NULL 则外查询也为空
(SELECT job_id FROM employees
WHERE last_name = 'Haas');
【练习8】非法使用子查询。
SELECT employee_id,last_name FROM employees
WHERE salary =
(SELECT MIN(salary) FROM employees GROUP BY department_id);
-- SELECT employee_id,last_name FROM employees
-- WHERE salary =
-- (SELECT MIN(salary) FROM employees GROUP BY department_id)
-- > Subquery returns more than 1 row
-- > 时间: 0.001s
三、多行子查询
也称为集合比较子查询,内查询返回多行,使用多行比较操作符。
in 等于列表中的任意一个
ANY 需要和单行比较操作符一起使用,和子查询返回的某一个值比较
ALL 需要和单行比较操作符一起使用,和子查询返回的所有值比较
SOME 实际上是ANY的别名,作用相同,一般常使用ANY
【练习9】返回其它job_id中比job_id为"IT_PROG"部门任一工资低的员工的员工号、姓名、job_id 以及salary。
#为了显示效果 使用LIMIT关键字限制条目数
mysql> 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' LIMIT 5;
+-------------+-----------+------------+---------+
| employee_id | last_name | job_id | salary |
+-------------+-----------+------------+---------+
| 206 | Gietz | AC_ACCOUNT | 8300.00 |
| 200 | Whalen | AD_ASST | 4400.00 |
| 110 | Chen | FI_ACCOUNT | 8200.00 |
| 111 | Sciarra | FI_ACCOUNT | 7700.00 |
| 112 | Urman | FI_ACCOUNT | 7800.00 |
+-------------+-----------+------------+---------+
5 rows in set (0.00 sec)
如下图所示:
【练习10】返回其它job_id中比job_id为"IT_PROG"部门所有工资低的员工的员工号、姓名、job_id 以及salary。
mysql> 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' LIMIT 5;
+-------------+------------+----------+---------+
| employee_id | last_name | job_id | salary |
+-------------+------------+----------+---------+
| 115 | Khoo | PU_CLERK | 3100.00 |
| 116 | Baida | PU_CLERK | 2900.00 |
| 117 | Tobias | PU_CLERK | 2800.00 |
| 118 | Himuro | PU_CLERK | 2600.00 |
| 119 | Colmenares | PU_CLERK | 2500.00 |
+-------------+------------+----------+---------+
5 rows in set (0.00 sec)
【练习11】查询平均工资最低的部门id。
mysql> #方式1
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) AS temp_emp);
+---------------+
| department_id |
+---------------+
| 50 |
+---------------+
1 row in set (0.00 sec)
mysql> #方式2
mysql> SELECT department_id FROM employees
-> GROUP BY department_id HAVING AVG(salary) <= ALL (
-> SELECT AVG(salary) avg_sal FROM employees
-> GROUP BY department_id);
+---------------+
| department_id |
+---------------+
| 50 |
+---------------+
1 row in set (0.00 sec)
【练习12】空值问题。
mysql> SELECT last_name FROM employees
-> WHERE employee_id NOT IN (
-> SELECT manager_id FROM employees
-> );
Empty set (0.00 sec)
#字句中有NULL
mysql> SELECT manager_id FROM employees;
+------------+
| manager_id |
+------------+
| NULL |
| 100 |
四、相关子查询
如果子查询的执行依赖于外部查询,通常情况下都是因为子查询中的表用到了外部的表,并进行了 条件关联
, 因此每执行一次外部查询,子查询都要重新计算一次,这样的子查询就称之为 关联子查询。 相关子查询按照一行接一行的顺序执行,主查询的每一行都执行一次子查询。
子查询中使用主查询中的列,说明如下:
【练习13】查询员工中工资大于本部门平均工资的员工的last_name,salary和其department_id。
#方式1
mysql> SELECT last_name,salary,department_id
-> FROM employees `outer`
-> WHERE salary > (SELECT AVG(salary) FROM employees WHERE department_id=`outer`.department_id) LIMIT 5以上是关于十一子查询详解的主要内容,如果未能解决你的问题,请参考以下文章