MYSQL数据库基础--MySQL子查询怎么操作?

Posted qian-fen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MYSQL数据库基础--MySQL子查询怎么操作?相关的知识,希望对你有一定的参考价值。

前言

从今天开始本系列文章就带各位小伙伴学习数据库技术。数据库技术是Java开发中必不可少的一部分知识内容。也是非常重要的技术。本系列教程由浅入深, 全面讲解数据库体系。 非常适合零基础的小伙伴来学习。


全文大约 【1258】字,不说废话,只讲可以让你学到技术、明白原理的纯干货!本文带有丰富案例及配图,让你更好的理解和运用文中的技术概念,并可以给你带来具有足够启迪的思考......

一. 子查询

什么是子查询呢?健哥来为各位小伙伴细细道来。所谓子查询就是将一个查询结果作为判断条件或者作为一张虚拟表在这个结果的基础上进行另一个查询。

1.子查询(作为条件判断)

下面就是将查询结果作为另一个查询的判断条件。

语法 : SELECT 列名 FROM 表名 Where 条件 (子查询结果)

举个例子:查询和健哥同岁的其他同学。

#1.先查询到健哥的年龄
select Sage from student where Sname=\'健哥\'; #年龄是12

#2.查询年龄等于健哥年龄的同学
select * from student where Sage=12;

#3.将 1、2 两条语句整合
select * from student where Sage=(select Sage from student where Sname=\'健哥\');

注意 : 将子查询 ”一行一列“的结果作为外部查询的条件,做第二次查询子查询得到一行一列的结果才能作为外部查询的等值判断条件或不等值条件判断。

2.子查询(作为枚举查询条件)

子查询作为枚举查询条件使用是将一个查询结果(单列多行)的结果集作为枚举查询条件进行二次查询。

语法 :SELECT 列名 FROM 表名 Where 列名 in(子查询结果);

举个栗子:查询和健哥,旭哥同龄的学生信息。

#思路:
#1. 先查询健哥和旭哥的年龄(多行单列)
select Sage from student where Sname in(\'健哥\', \'旭哥\'); #年龄是12和23

#2. 再查询年龄为12和23的学员信息
select * from student where Sage in(12, 23); 

#3.SQL:合并
select * from student where Sage in(select Sage from student where Sname in(\'健哥\', \'旭哥\')); 

将子查询 ”多行一列“的结果作为外部查询的枚举查询条件,做第二次查询。

3.子查询(作为一张表)

此处健哥敲黑板啦!非常重要,使用频率很高!

下面的子查询是将一个查询的结果当做一张虚拟表,然后在这个表的结果基础上再进行查询。

语法 :SELECT 列名 FROM (子查询的结果集) WHERE 条件;

举个栗子:查询最老的5名学生信息。

#思路:
#1. 先对学生年龄排序查询(排序后的临时表)
select * from student order by Sage desc;

#2. 再查询临时表中前5行学生信息
select *
from (临时表) 
limit 0,5;

#SQL:合并
select * from (
		select * from student order by Sage desc
) as temp limit 0,4;

将子查询 ”多行多列“的结果作为外部查询的一张表,做第二次查询。

注意: 子查询作为临时表,为其赋予一个临时表名。


二. 结语

最后在这里对本文核心要点进行总结:

  1. 子查询有三种使用场景分别是:子查询结果作为判断条件、子查询结果作为枚举条件、子查询结果作为一个虚拟表进行二次查询。

  2. 以上三种使用方式各位小伙伴要熟练掌握哦。

十一子查询详解

文章目录

前置知识:

一、数据库开发与实战专栏导学及数据库基础概念入门
二、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以上是关于MYSQL数据库基础--MySQL子查询怎么操作?的主要内容,如果未能解决你的问题,请参考以下文章

mysql-1.1基础

MySQL基础语法之子链接查询和特殊查询(union 和 limit)

十一子查询详解

十一子查询详解

MySQL入门

Mysql 子查询怎么写?