当前位置: 首页 > news >正文

【MySQL基础篇重点】八、复合查询

文章目录

  • Ⅰ. 基本查询回顾
      • 1、查询工资高于500或岗位为MANAGER的雇员,同时还要满足他们的姓名首字母为大写的J
      • 2、按照部门号升序而雇员的工资降序排序
      • 3、使用年薪进行降序排序
      • 4、显示工资最高的员工的名字和工作岗位
      • 5、显示工资高于平均工资的员工信息
      • 6、显示每个部门的平均工资和最高工资
      • 7、显示平均工资低于2000的部门号和它的平均工资
      • 8、显示每种岗位的雇员总数,平均工资
  • Ⅱ. 多表查询
      • 1、显示雇员名、雇员工资、部门编号以及所在部门的名字
      • 2、显示部门号为10的部门名,员工名和工资
      • 3、显示各个员工的姓名,工资,及工资级别
  • Ⅲ. 自连接
    • 显示员工 FORD 的上级领导的编号和姓名
      • ① 子查询方式
      • ② 自连接方式
  • Ⅳ. 子查询
    • 🎏在 `where` 子句中的子查询
      • 1、单行子查询
        • 案例:显示与SMITH同一部门的员工
      • 2、多行子查询
        • ① **==`in` 关键字==**:查询和10号部门的工作岗位相同的雇员的名字,岗位,工资,部门号(但是不包含10号自己),并且显示对应的员工属于哪一个部门的名字
        • ② **==`all` 关键字==**:显示工资比部门30的所有员工的工资高的员工的姓名、工资和部门号
        • ③ **==`any`关键字==**:显示工资比部门30的任意员工的工资高的员工的姓名、工资和部门号(包含自己部门的员工)
      • 3、多列子查询
        • 案例:查询和SMITH的部门编号和岗位完全相同的所有雇员,不含SMITH本人
    • 🎏 在 `from` 子句中的子查询
      • ① 显示每个部门中超过平均工资的员工的姓名、部门、工资、平均工资,以及所在位置
      • ② 查找每个部门工资最高的人的姓名、工资、部门、最高工资
      • ③ 显示每个部门的信息(部门名、编号、地址)和人员数量
  • Ⅴ. 合并查询
    • 1、`union`
        • 案例:将工资大于2500或职位是MANAGER的人找出来
    • 2、`union all`
        • 案例:将工资大于2500或职位是MANAGER的人找出来
  • 实战OJ

在这里插入图片描述

Ⅰ. 基本查询回顾

​ 前面我们学习的 mysql 表的查询都是对一张表进行查询,在实际开发中这远远不够,所以我们就需要学习下面的复合查询!

​ 在进行学习之前,我们先来简单的看些案例,巩固一下单表查询的操作!下面的操作案例中,使用的依然是表的增删查改笔记中的雇员信息表!

1、查询工资高于500或岗位为MANAGER的雇员,同时还要满足他们的姓名首字母为大写的J

-- 1、使用substring函数来截取第一个字符判断
mysql> select * from emp where (sal>500 or job='MANAGER') and substring(ename, 1, 1)='J';
+--------+-------+---------+------+---------------------+---------+------+--------+
| empno  | ename | job     | mgr  | hiredate            | sal     | comm | deptno |
+--------+-------+---------+------+---------------------+---------+------+--------+
| 007566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL |     20 |
| 007900 | JAMES | CLERK   | 7698 | 1981-12-03 00:00:00 |  950.00 | NULL |     30 |
+--------+-------+---------+------+---------------------+---------+------+--------+
2 rows in set (0.00 sec)-- 2、使用like进行模糊匹配来判断
mysql> select * from emp where (sal>500 or job='MANAGER') and ename like 'J%';
+--------+-------+---------+------+---------------------+---------+------+--------+
| empno  | ename | job     | mgr  | hiredate            | sal     | comm | deptno |
+--------+-------+---------+------+---------------------+---------+------+--------+
| 007566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL |     20 |
| 007900 | JAMES | CLERK   | 7698 | 1981-12-03 00:00:00 |  950.00 | NULL |     30 |
+--------+-------+---------+------+---------------------+---------+------+--------+
2 rows in set (0.00 sec)

2、按照部门号升序而雇员的工资降序排序

mysql> select * from emp order by deptno asc, sal desc;
+--------+--------+-----------+------+---------------------+---------+---------+--------+
| empno  | ename  | job       | mgr  | hiredate            | sal     | comm    | deptno |
+--------+--------+-----------+------+---------------------+---------+---------+--------+
| 007839 | KING   | PRESIDENT | NULL | 1981-11-17 00:00:00 | 5000.00 |    NULL |     10 |
| 007782 | CLARK  | MANAGER   | 7839 | 1981-06-09 00:00:00 | 2450.00 |    NULL |     10 |
| 007934 | MILLER | CLERK     | 7782 | 1982-01-23 00:00:00 | 1300.00 |    NULL |     10 |
| 007788 | SCOTT  | ANALYST   | 7566 | 1987-04-19 00:00:00 | 3000.00 |    NULL |     20 |
| 007902 | FORD   | ANALYST   | 7566 | 1981-12-03 00:00:00 | 3000.00 |    NULL |     20 |
| 007566 | JONES  | MANAGER   | 7839 | 1981-04-02 00:00:00 | 2975.00 |    NULL |     20 |
| 007876 | ADAMS  | CLERK     | 7788 | 1987-05-23 00:00:00 | 1100.00 |    NULL |     20 |
| 007369 | SMITH  | CLERK     | 7902 | 1980-12-17 00:00:00 |  800.00 |    NULL |     20 |
| 007698 | BLAKE  | MANAGER   | 7839 | 1981-05-01 00:00:00 | 2850.00 |    NULL |     30 |
| 007499 | ALLEN  | SALESMAN  | 7698 | 1981-02-20 00:00:00 | 1600.00 |  300.00 |     30 |
| 007844 | TURNER | SALESMAN  | 7698 | 1981-09-08 00:00:00 | 1500.00 |    0.00 |     30 |
| 007521 | WARD   | SALESMAN  | 7698 | 1981-02-22 00:00:00 | 1250.00 |  500.00 |     30 |
| 007654 | MARTIN | SALESMAN  | 7698 | 1981-09-28 00:00:00 | 1250.00 | 1400.00 |     30 |
| 007900 | JAMES  | CLERK     | 7698 | 1981-12-03 00:00:00 |  950.00 |    NULL |     30 |
+--------+--------+-----------+------+---------------------+---------+---------+--------+
14 rows in set (0.00 sec)

3、使用年薪进行降序排序

​ 这里的年薪我们按照十二个月基本薪资加上奖金来算的,但是因为奖金有没有是 null,所以想要用 ifnull() 函数来判断一下,不然直接相加的话会变成 null

mysql> select ename, sal*12+ifnull(comm,0) 年薪 from emp order by 年薪 desc;
+--------+----------+
| ename  | 年薪     |
+--------+----------+
| KING   | 60000.00 |
| SCOTT  | 36000.00 |
| FORD   | 36000.00 |
| JONES  | 35700.00 |
| BLAKE  | 34200.00 |
| CLARK  | 29400.00 |
| ALLEN  | 19500.00 |
| TURNER | 18000.00 |
| MARTIN | 16400.00 |
| MILLER | 15600.00 |
| WARD   | 15500.00 |
| ADAMS  | 13200.00 |
| JAMES  | 11400.00 |
| SMITH  |  9600.00 |
+--------+----------+
14 rows in set (0.00 sec)

4、显示工资最高的员工的名字和工作岗位

​ 下面演示三种方式,其中后两者使用了一种复合查询的一种方式:子查询

​ 其实就像是一个嵌套,先执行括号内的子查询语句,然后返回的结果再用来执行外层的查询语句,这个我们下面会讲!

-- 1、通过排序+limit来筛选出工资最高
mysql> select ename,job,sal from emp order by sal desc limit 1;
+-------+-----------+---------+
| ename | job       | sal     |
+-------+-----------+---------+
| KING  | PRESIDENT | 5000.00 |
+-------+-----------+---------+
1 row in set (0.00 sec)-- 2、使用子查询,通过排序+limit来筛选出工资最高
mysql> select ename,job,sal from emp where sal=(select sal from emp order by sal desc limit 1);
+-------+-----------+---------+
| ename | job       | sal     |
+-------+-----------+---------+
| KING  | PRESIDENT | 5000.00 |
+-------+-----------+---------+
1 row in set (0.01 sec)-- 3、使用子查询,通过使用max函数筛选工资最高
mysql> select ename,job,sal from emp where sal=(select max(sal) from emp);
+-------+-----------+---------+
| ename | job       | sal     |
+-------+-----------+---------+
| KING  | PRESIDENT | 5000.00 |
+-------+-----------+---------+
1 row in set (0.00 sec)

5、显示工资高于平均工资的员工信息

​ 像这种类型的语句,其实用子查询会更方便一些,由子查询语句完成平均工资,然后通过外层的 where 条件进行筛选出工资大于子查询得到的平均工资的员工消息:

mysql> select avg(sal) from emp;
+-------------+
| avg(sal)    |
+-------------+
| 2073.214286 |
+-------------+
1 row in set (0.00 sec)mysql> select * from emp where sal > (select avg(sal) from emp);
+--------+-------+-----------+------+---------------------+---------+------+--------+
| empno  | ename | job       | mgr  | hiredate            | sal     | comm | deptno |
+--------+-------+-----------+------+---------------------+---------+------+--------+
| 007566 | JONES | MANAGER   | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL |     20 |
| 007698 | BLAKE | MANAGER   | 7839 | 1981-05-01 00:00:00 | 2850.00 | NULL |     30 |
| 007782 | CLARK | MANAGER   | 7839 | 1981-06-09 00:00:00 | 2450.00 | NULL |     10 |
| 007788 | SCOTT | ANALYST   | 7566 | 1987-04-19 00:00:00 | 3000.00 | NULL |     20 |
| 007839 | KING  | PRESIDENT | NULL | 1981-11-17 00:00:00 | 5000.00 | NULL |     10 |
| 007902 | FORD  | ANALYST   | 7566 | 1981-12-03 00:00:00 | 3000.00 | NULL |     20 |
+--------+-------+-----------+------+---------------------+---------+------+--------+
6 rows in set (0.00 sec)

6、显示每个部门的平均工资和最高工资

​ 很明显,因为提到了每个部门,所以需要先做分组,然后再聚合统计!

mysql> select deptno, avg(sal) 平均工资, max(sal) 最高工资 from emp group by deptno;
+--------+--------------+--------------+
| deptno | 平均工资     | 最高工资     |
+--------+--------------+--------------+
|     10 |  2916.666667 |      5000.00 |
|     20 |  2175.000000 |      3000.00 |
|     30 |  1566.666667 |      2850.00 |
+--------+--------------+--------------+
3 rows in set (0.00 sec)

7、显示平均工资低于2000的部门号和它的平均工资

​ 从题目要求可以看出还是需要对部门进行分组的,然后进行聚合统计平均工资,最后通过限定条件也就是小于两千块钱的进行显示:

mysql> select deptno, avg(sal) 平均工资 from emp group by deptno having 平均工资<2000;
+--------+--------------+
| deptno | 平均工资     |
+--------+--------------+
|     30 |  1566.666667 |
+--------+--------------+
1 row in set (0.00 sec)

8、显示每种岗位的雇员总数,平均工资

mysql> select job, count(*) 雇员总数, avg(sal) 平均工资 from emp group by job;
+-----------+--------------+--------------+
| job       | 雇员总数     | 平均工资     |
+-----------+--------------+--------------+
| ANALYST   |            2 |  3000.000000 |
| CLERK     |            4 |  1037.500000 |
| MANAGER   |            3 |  2758.333333 |
| PRESIDENT |            1 |  5000.000000 |
| SALESMAN  |            4 |  1400.000000 |
+-----------+--------------+--------------+
5 rows in set (0.00 sec)

Ⅱ. 多表查询

​ 实际开发中往往数据来自不同的表,所以需要多表查询。

​ 在 mysql 中,多表查询是指在一个查询语句中同时使用多个表来获取所需的数据,这可以通过使用 join 子句来实现,比如常见的内连接、左连接、右连接、全连接、自连接等等多表查询方式!

​ 下面我们不多废话,直接用一个简单的公司管理系统,有三张表 empdeptsalgrade 来演示如何进行最简单的多表查询!(这三张依旧是我们在增删查改中使用的样例表)

1、显示雇员名、雇员工资、部门编号以及所在部门的名字

​ 因为上面的数据来自 empdept 两张表,所以我们需要将两张表一起查询。

​ 其实非常简单,只需要 from 语句添上需要查询的表即可,表与表之间用逗号隔开,如下所示:

在这里插入图片描述

​ 其实细心的同学已经能看出来,其实有这么多重复的行数据,是因为多个表的联合,是通过 穷举组合的方式联合的,在数学中,这种其实称为笛卡尔积,尚且不需要想的那么高大上,其实就是将两张表的每行元素进行枚举组合罢了,如下图所示:

在这里插入图片描述

​ 但其实我们也知道,虽然得到的结果表有很多行数据,但是重复的并且没有意义的行数据有很多。比如说这里两张表的关联列字段是 deptno 也就是部门编号,那么肯定一个人的部门编号是一一对应的,那么那些不对应的编号其实就没有意义了,所以我们可以使用 where 语句进行筛选:

在这里插入图片描述

​ 这才是我们需要的嘛!

​ 最后我们根据题目要求,将特定的字段查询出来即可:

mysql> select ename, sal, emp.deptno, dname from emp, dept where emp.deptno=dept.deptno;
+--------+---------+--------+------------+
| ename  | sal     | deptno | dname      |
+--------+---------+--------+------------+
| SMITH  |  800.00 |     20 | RESEARCH   |
| ALLEN  | 1600.00 |     30 | SALES      |
| WARD   | 1250.00 |     30 | SALES      |
| JONES  | 2975.00 |     20 | RESEARCH   |
| MARTIN | 1250.00 |     30 | SALES      |
| BLAKE  | 2850.00 |     30 | SALES      |
| CLARK  | 2450.00 |     10 | ACCOUNTING |
| SCOTT  | 3000.00 |     20 | RESEARCH   |
| KING   | 5000.00 |     10 | ACCOUNTING |
| TURNER | 1500.00 |     30 | SALES      |
| ADAMS  | 1100.00 |     20 | RESEARCH   |
| JAMES  |  950.00 |     30 | SALES      |
| FORD   | 3000.00 |     20 | RESEARCH   |
| MILLER | 1300.00 |     10 | ACCOUNTING |
+--------+---------+--------+------------+
14 rows in set (0.00 sec)

​ 其中如果 在联合后的表中有重名的字段,要访问它的时候需要使用 “表名.列字段名” 的形式去访问,而不重名的字段可以不管!

2、显示部门号为10的部门名,员工名和工资

mysql> select emp.deptno, dname, ename, sal from emp, dept where emp.deptno=dept.deptno and emp.deptno=10;
+--------+------------+--------+---------+
| deptno | dname      | ename  | sal     |
+--------+------------+--------+---------+
|     10 | ACCOUNTING | CLARK  | 2450.00 |
|     10 | ACCOUNTING | KING   | 5000.00 |
|     10 | ACCOUNTING | MILLER | 1300.00 |
+--------+------------+--------+---------+
3 rows in set (0.00 sec)

3、显示各个员工的姓名,工资,及工资级别

​ 这次用到的表是 empsalgrade 两张表,其它的操作都是一样的,只不过在筛选的时候,我们需要筛选的是对应的等级,就需要根据工资在哪个区间来判断,而工资等级区间是由 losalhisal 来维护的!

mysql> select ename, sal, grade, losal, hisal from emp, salgrade where sal between losal and hisal;
+--------+---------+-------+-------+-------+
| ename  | sal     | grade | losal | hisal |
+--------+---------+-------+-------+-------+
| SMITH  |  800.00 |     1 |   700 |  1200 |
| ALLEN  | 1600.00 |     3 |  1401 |  2000 |
| WARD   | 1250.00 |     2 |  1201 |  1400 |
| JONES  | 2975.00 |     4 |  2001 |  3000 |
| MARTIN | 1250.00 |     2 |  1201 |  1400 |
| BLAKE  | 2850.00 |     4 |  2001 |  3000 |
| CLARK  | 2450.00 |     4 |  2001 |  3000 |
| SCOTT  | 3000.00 |     4 |  2001 |  3000 |
| KING   | 5000.00 |     5 |  3001 |  9999 |
| TURNER | 1500.00 |     3 |  1401 |  2000 |
| ADAMS  | 1100.00 |     1 |   700 |  1200 |
| JAMES  |  950.00 |     1 |   700 |  1200 |
| FORD   | 3000.00 |     4 |  2001 |  3000 |
| MILLER | 1300.00 |     2 |  1201 |  1400 |
+--------+---------+-------+-------+-------+
14 rows in set (0.00 sec)

Ⅲ. 自连接

​ 自连接指的是 将表与自身进行连接,一般用于处理具有层次结构的数据。

​ 比如说这里我们拿一张小一点的表 salgrade 来试试看自连接:

mysql> select * from salgrade, salgrade;
ERROR 1066 (42000): Not unique table/alias: 'salgrade'

​ 它直接给我们报了一个错误,其实是因为如果我们直接拿同一张去连接同一张表,是会有表的命名冲突的,所以我们 对于同一张表的自连接必须要进行重命名

mysql> select * from salgrade t1, salgrade t2;
+-------+-------+-------+-------+-------+-------+
| grade | losal | hisal | grade | losal | hisal |
+-------+-------+-------+-------+-------+-------+
|     1 |   700 |  1200 |     1 |   700 |  1200 |
|     2 |  1201 |  1400 |     1 |   700 |  1200 |
|     3 |  1401 |  2000 |     1 |   700 |  1200 |
|     4 |  2001 |  3000 |     1 |   700 |  1200 |
|     5 |  3001 |  9999 |     1 |   700 |  1200 |
|     1 |   700 |  1200 |     2 |  1201 |  1400 |
|     2 |  1201 |  1400 |     2 |  1201 |  1400 |
|     3 |  1401 |  2000 |     2 |  1201 |  1400 |
|     4 |  2001 |  3000 |     2 |  1201 |  1400 |
|     5 |  3001 |  9999 |     2 |  1201 |  1400 |
|     1 |   700 |  1200 |     3 |  1401 |  2000 |
|     2 |  1201 |  1400 |     3 |  1401 |  2000 |
|     3 |  1401 |  2000 |     3 |  1401 |  2000 |
|     4 |  2001 |  3000 |     3 |  1401 |  2000 |
|     5 |  3001 |  9999 |     3 |  1401 |  2000 |
|     1 |   700 |  1200 |     4 |  2001 |  3000 |
|     2 |  1201 |  1400 |     4 |  2001 |  3000 |
|     3 |  1401 |  2000 |     4 |  2001 |  3000 |
|     4 |  2001 |  3000 |     4 |  2001 |  3000 |
|     5 |  3001 |  9999 |     4 |  2001 |  3000 |
|     1 |   700 |  1200 |     5 |  3001 |  9999 |
|     2 |  1201 |  1400 |     5 |  3001 |  9999 |
|     3 |  1401 |  2000 |     5 |  3001 |  9999 |
|     4 |  2001 |  3000 |     5 |  3001 |  9999 |
|     5 |  3001 |  9999 |     5 |  3001 |  9999 |
+-------+-------+-------+-------+-------+-------+
25 rows in set (0.00 sec)

​ 并且我们可以看到确实是符合笛卡尔积的枚举形式的!

​ 此外,我们是可以 from 子句中进行重命名的,并且这样子操作后几乎其它位置的子句都能使用该别名,因为 from 子句的执行优先级很高!

​ 下面还是通过样例来加深这个概念的理解!

显示员工 FORD 的上级领导的编号和姓名

​ 其中表 emp 中的列字段 mgr 是员工领导的编号,而每个人的编号是列字段 empno

​ 主要的思路很简单:第一步就是找到 FORD 这个人它的 mgr 字段;第二步就是通过 mgr 字段找到对应的 empno 字段,其对应的就是 FORD 的老板!

​ 我们一共有两种做法,一种是子查询,一种是自连接,我们两种都来介绍一下:

① 子查询方式

mysql> select empno 员工编号, ename 姓名 from emp where empno=( select mgr from emp where ename='FORD' );
+--------------+--------+
| 员工编号     | 姓名   |
+--------------+--------+
|       007566 | JONES  |
+--------------+--------+
1 row in set (0.00 sec)

② 自连接方式

mysql> select e2.empno, e2.ename from emp e1, emp e2 where e1.ename='FORD' and e2.empno=e1.mgr;
+--------+-------+
| empno  | ename |
+--------+-------+
| 007566 | JONES |
+--------+-------+
1 row in set (0.00 sec)

​ 如果我们在 select 语句中不指定 e2 的话,那么查询出来的就是 e1e2 的消息,那么就会报错,因为 mysql 不知道我们要的是哪个表的 empno,如下所示:

mysql> select empno, ename from emp e1, emp e2 where e1.ename='FORD' and e2.empno=e1.mgr;
ERROR 1052 (23000): Column 'empno' in field list is ambiguous

Ⅳ. 子查询

​ 子查询是指嵌入在其他 sql 语句中的 select 查询语句,也叫 嵌套查询,相当于是一个函数调用一样。它可以作为主查询的一部分,用于提供更详细或特定的数据,以便满足查询的条件或限制。

​ 子查询通常用于以下几种情况:

  1. 过滤条件:子查询可以用于过滤主查询的结果。例如,你可以使用子查询来查找满足某个条件的记录,然后将这些记录作为主查询的结果返回。
  2. 聚合函数:子查询可以用于计算聚合函数的结果。例如,你可以使用子查询来计算平均值、总和或计数等聚合函数的结果,并将其作为主查询的一部分返回。
  3. 子查询作为表:子查询可以作为表来使用,以便在主查询中进行连接操作或其他复杂的查询操作。例如,你可以使用子查询作为一个临时表,然后将其与其他表进行连接,以获取更复杂的查询结果。

​ 在子查询中,你可以使用主查询中的字段或条件,以及其他适用的查询语句。

🎏在 where 子句中的子查询

​ 下面讲的三种子查询的方式,都是位于 where 子句中的,后面我们还会讲子查询出现再 from 子句中的,它们其实功能差不多,但是含义却大不相同!下面我们先介绍前者!

1、单行子查询

​ 其实这在之前我们就使用过了,其是 用于返回一行记录的子查询

​ 下面举个例子就懂了!

案例:显示与SMITH同一部门的员工

​ 思路很简单,首先就是要找出 SMITH 在哪个部门,拿到其部门编号;然后通过该部门编号筛选出该部门编号内的员工。

mysql> select * from emp where deptno=( select deptno from emp where ename='SMITH' );
+--------+-------+---------+------+---------------------+---------+------+--------+
| empno  | ename | job     | mgr  | hiredate            | sal     | comm | deptno |
+--------+-------+---------+------+---------------------+---------+------+--------+
| 007369 | SMITH | CLERK   | 7902 | 1980-12-17 00:00:00 |  800.00 | NULL |     20 |
| 007566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL |     20 |
| 007788 | SCOTT | ANALYST | 7566 | 1987-04-19 00:00:00 | 3000.00 | NULL |     20 |
| 007876 | ADAMS | CLERK   | 7788 | 1987-05-23 00:00:00 | 1100.00 | NULL |     20 |
| 007902 | FORD  | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.00 | NULL |     20 |
+--------+-------+---------+------+---------------------+---------+------+--------+
5 rows in set (0.00 sec)

2、多行子查询

​ 用于 返回多行记录的子查询。简单地说,就是子查询的结果是多行的罢了!

​ 还是一样,下面通过举例子来理解,顺序学习几个常用的关键字!

in 关键字:查询和10号部门的工作岗位相同的雇员的名字,岗位,工资,部门号(但是不包含10号自己),并且显示对应的员工属于哪一个部门的名字

​ 首先我们要先知道 10 号部门有什么工作岗位:

select job from emp where deptno=10;

​ 接着就是查询表中雇员的名字、岗位、工资、部门号:

select ename, job, sal, deptno from emp;

​ 此时我们将两者结合起来,这里需要使用一个 in 关键字,其作用是在查询中指定一个条件,以便匹配多个值,它通常与 where 子句一起使用。

​ 因为我们要查询表中的那些与 10 号部门有相同工作岗位的信息,所以指定条件就是 job ,所以应该是这样子表示:

mysql> select ename, job, sal, deptno from emp where job in (select job from emp where deptno = 10);
+--------+-----------+---------+--------+
| ename  | job       | sal     | deptno |
+--------+-----------+---------+--------+
| JONES  | MANAGER   | 2975.00 |     20 |
| BLAKE  | MANAGER   | 2850.00 |     30 |
| CLARK  | MANAGER   | 2450.00 |     10 |
| KING   | PRESIDENT | 5000.00 |     10 |
| SMITH  | CLERK     |  800.00 |     20 |
| ADAMS  | CLERK     | 1100.00 |     20 |
| JAMES  | CLERK     |  950.00 |     30 |
| MILLER | CLERK     | 1300.00 |     10 |
+--------+-----------+---------+--------+
8 rows in set (0.00 sec)

​ 就相当于我们拿了一张完整的但是只带名字、岗位、工资、部门号的 表1,然后又拿了一张 10 号部门的工作岗位表2用来筛选出当前 表1 需要的行数据!

​ 然后我们进行 deptno 的筛选,去除 deptno = 10 的那些行数据:

mysql> select ename, job, sal, deptno from emp where job in (select job from emp where deptno = 10) and deptno<>10;
+-------+---------+---------+--------+
| ename | job     | sal     | deptno |
+-------+---------+---------+--------+
| JONES | MANAGER | 2975.00 |     20 |
| BLAKE | MANAGER | 2850.00 |     30 |
| SMITH | CLERK   |  800.00 |     20 |
| ADAMS | CLERK   | 1100.00 |     20 |
| JAMES | CLERK   |  950.00 |     30 |
+-------+---------+---------+--------+
5 rows in set (0.00 sec)

​ 最后还有一个条件,就是显示对应的员工属于哪一个部门的名字,这是在另一张表 dept 中的一个字段 dname,所以这就和多表查询结合在一起!

​ 其实不难,我们可以把当前已经筛选完的这个表,作为一张新的表进行返回,相当于又是一层嵌套,然后对其进行重命名就能与 dept 进行多表查询拿到想要的结果了,其 sql 语句如下所示:

mysql> select ename, job, sal, tmp.deptno, dname -> from (select ename, job, sal, deptno from emp where job in (select job from emp where deptno<=>10) and deptno<>10) as tmp, dept -> where tmp.deptno<=>dept.deptno;
+-------+---------+---------+--------+----------+
| ename | job     | sal     | deptno | dname    |
+-------+---------+---------+--------+----------+
| SMITH | CLERK   |  800.00 |     20 | RESEARCH |
| JONES | MANAGER | 2975.00 |     20 | RESEARCH |
| ADAMS | CLERK   | 1100.00 |     20 | RESEARCH |
| BLAKE | MANAGER | 2850.00 |     30 | SALES    |
| JAMES | CLERK   |  950.00 |     30 | SALES    |
+-------+---------+---------+--------+----------+
5 rows in set (0.00 sec)

在这里插入图片描述

​ 这里很容易搞混,所以要理清楚,这最后一步操作也涉及到了下面会讲的 多列子查询,这里提前引出来!

​ 还要强调的一点就是 mysql 中一切皆表”,也就是说我们每次查询出来的一个结果,其实都可以看作一个表,所以才允许我们做这些操作!

​ 并且除了 in 关键字之外,还可以在其前面加上 not,即 not in 表示不存在该子查询表中的结果

all 关键字:显示工资比部门30的所有员工的工资高的员工的姓名、工资和部门号

​ 首先我们要先知道 30 号部门所有员工的工资:

select sal from emp where dept=30;

​ 接着就是查询表中雇员的名字、工资、部门号:

select ename, sal, deptno from emp;

​ 此时我们要将两者结合起来,需要使用 all 关键字,其用于比较一个值与子查询返回的所有值是否都满足某个条件

​ 注意,all 关键字只能与比较运算符一起使用,不能与其他类型的运算符(如逻辑运算符)一起使用。

​ 因为我们要的是比部门号为 30 的员工工资高的结果,所以需要比较的是工资,并且要大于,所以应该是这样子表示:

mysql> select ename, sal, deptno from emp where sal > all(select sal from emp where deptno=30);
+-------+---------+--------+
| ename | sal     | deptno |
+-------+---------+--------+
| JONES | 2975.00 |     20 |
| SCOTT | 3000.00 |     20 |
| KING  | 5000.00 |     10 |
| FORD  | 3000.00 |     20 |
+-------+---------+--------+
4 rows in set (0.00 sec)

​ 下面是不使用 all 关键字的做法,其实就是用一个 max() 函数筛选出 30 号部门的最高薪资,然后对比这个最高薪资即可,就不需要列举出 30 号部门所有员工的工资了!

select * from emp where sal > (select max(sal) from emp where deptno=30);
any关键字:显示工资比部门30的任意员工的工资高的员工的姓名、工资和部门号(包含自己部门的员工)

​ 在 mysql 中,any 是一个用于比较的关键字,其用法和 all 关键字基本是一样的,只不过 any 关键字的作用是将比较运算符应用于一组值,并返回与任何一个值匹配的行。

​ 只要掌握了样例二的操作,那么这里也是没有问题,就不再赘述了,语句如下所示:

mysql> select ename, sal, deptno from emp where sal > any (select sal from emp where deptno=30);
+--------+---------+--------+
| ename  | sal     | deptno |
+--------+---------+--------+
| ALLEN  | 1600.00 |     30 |
| WARD   | 1250.00 |     30 |
| JONES  | 2975.00 |     20 |
| MARTIN | 1250.00 |     30 |
| BLAKE  | 2850.00 |     30 |
| CLARK  | 2450.00 |     10 |
| SCOTT  | 3000.00 |     20 |
| KING   | 5000.00 |     10 |
| TURNER | 1500.00 |     30 |
| ADAMS  | 1100.00 |     20 |
| FORD   | 3000.00 |     20 |
| MILLER | 1300.00 |     10 |
+--------+---------+--------+
12 rows in set (0.01 sec)

3、多列子查询

​ 我们前面讲的单行子查询是指子查询只返回单列、单行数据;多行子查询是指返回单列多行数据,都是针对单列而言的。而这里讲的 多列子查询 则是指 查询返回多个列数据 的子查询语句。

​ 下面结合案例来理解!

案例:查询和SMITH的部门编号和岗位完全相同的所有雇员,不含SMITH本人

​ 首先我们先查询 SMITH 的部门编号以及岗位:

select deptno, job from emp where ename='SMITH';

​ 可以发现此时上面的 select 语句中有两个列字段,也就说这个筛选完后的表,是一个多列的表,那么我们在外层的 where 语句中进行比较的时候,如果想一步到位同时比较部门编号和岗位的话,可以 用一个 () 圆括号将多个列字段括起来,与筛选完后的表进行判断比较,如下所示:

mysql> select * from emp where (deptno, job) = (select deptno, job from emp where ename='SMITH');
+--------+-------+-------+------+---------------------+---------+------+--------+
| empno  | ename | job   | mgr  | hiredate            | sal     | comm | deptno |
+--------+-------+-------+------+---------------------+---------+------+--------+
| 007369 | SMITH | CLERK | 7902 | 1980-12-17 00:00:00 |  800.00 | NULL |     20 |
| 007876 | ADAMS | CLERK | 7788 | 1987-05-23 00:00:00 | 1100.00 | NULL |     20 |
+--------+-------+-------+------+---------------------+---------+------+--------+
2 rows in set (0.00 sec)

​ 其实这里的 = 等号换成关键字 in 也是可以的,因为都是比较相同的结果!如下所示:

mysql> select * from emp where (deptno, job) in (select deptno, job from emp where ename='SMITH');
+--------+-------+-------+------+---------------------+---------+------+--------+
| empno  | ename | job   | mgr  | hiredate            | sal     | comm | deptno |
+--------+-------+-------+------+---------------------+---------+------+--------+
| 007369 | SMITH | CLERK | 7902 | 1980-12-17 00:00:00 |  800.00 | NULL |     20 |
| 007876 | ADAMS | CLERK | 7788 | 1987-05-23 00:00:00 | 1100.00 | NULL |     20 |
+--------+-------+-------+------+---------------------+---------+------+--------+
2 rows in set (0.00 sec)

​ 最后再添加一个限定条件也就是不能包含 SMITH 本人,这个比较简单:

mysql> select * from emp where (deptno, job) = (select deptno, job from emp where ename='SMITH') and ename<>'SMITH';
+--------+-------+-------+------+---------------------+---------+------+--------+
| empno  | ename | job   | mgr  | hiredate            | sal     | comm | deptno |
+--------+-------+-------+------+---------------------+---------+------+--------+
| 007876 | ADAMS | CLERK | 7788 | 1987-05-23 00:00:00 | 1100.00 | NULL |     20 |
+--------+-------+-------+------+---------------------+---------+------+--------+
1 row in set (0.00 sec)

🎏 在 from 子句中的子查询

​ 子查询语句可以出现在 from 子句中,这里要用到数据查询的技巧,把一个子查询当做一个临时表使用,这相当于是 子查询 + 多表查询

​ 下面通过案例来加深理解!

① 显示每个部门中超过平均工资的员工的姓名、部门、工资、平均工资,以及所在位置

​ 如果说这里我们先用子查询找出每个部门的平均工资,然后通过 where 子句进行每个部门内数据的单独的筛选,那其实操作起来是比较麻烦的!

​ 这是因为我们现在要查的部门有多个,而前面我们讲 where 子句中的子查询,其实更适合用于针对某个部门的筛选,对于多个部门或者 多个字段的共同筛选的话,其实要用 from 子句中的子查询会更加方便

​ 首先还是一样,我们得用子查询找出每个部门的平均工资:

mysql> select deptno, avg(sal) -> from emp group by deptno;
+--------+-------------+
| deptno | avg(sal)    |
+--------+-------------+
|     10 | 2916.666667 |
|     20 | 2175.000000 |
|     30 | 1566.666667 |
+--------+-------------+

​ 接着就是最重要的环节了,将当前我们这个平均工资表重命名为 tmp(注意,在多表查询的时候用到子查询得到的新表是想要重命名才能使用的,因为它是一个临时表,没有重命名的话是找不到的),与员工表 emp 进行笛卡尔积,也就是多表查询:

mysql> select * -> from emp, (select deptno, avg(sal) from emp group by deptno) as tmp-> ;
+--------+--------+-----------+------+---------------------+---------+---------+--------+--------+-------------+
| empno  | ename  | job       | mgr  | hiredate            | sal     | comm    | deptno | deptno | avg(sal)    |
+--------+--------+-----------+------+---------------------+---------+---------+--------+--------+-------------+
| 007369 | SMITH  | CLERK     | 7902 | 1980-12-17 00:00:00 |  800.00 |    NULL |     20 |     10 | 2916.666667 |
| 007499 | ALLEN  | SALESMAN  | 7698 | 1981-02-20 00:00:00 | 1600.00 |  300.00 |     30 |     10 | 2916.666667 |
| 007902 | FORD   | ANALYST   | 7566 | 1981-12-03 00:00:00 | 3000.00 |    NULL |     20 |     10 | 2916.666667 |
| 007369 | SMITH  | CLERK     | 7902 | 1980-12-17 00:00:00 |  800.00 |    NULL |     20 |     20 | 2175.000000 |
| 007499 | ALLEN  | SALESMAN  | 7698 | 1981-02-20 00:00:00 | 1600.00 |  300.00 |     30 |     20 | 2175.000000 |
| 007654 | MARTIN | SALESMAN  | 7698 | 1981-09-28 00:00:00 | 1250.00 | 1400.00 |     30 |     20 | 2175.000000 |
……

​ 这样子一来得到的新表中的每个员工都会匹配上 tmp 中的每一个平均值,这我们可以通过 where 条件筛选去掉没意义的部分,也就是部门编号不匹配的那些行记录:

mysql> select ename, tmp.deptno, sal, 平均工资  -> from emp, (select deptno, avg(sal) 平均工资 from emp group by deptno) as tmp -> where emp.deptno<=>tmp.deptno-> ;
+--------+--------+---------+--------------+
| ename  | deptno | sal     | 平均工资     |
+--------+--------+---------+--------------+
| SMITH  |     20 |  800.00 |  2175.000000 |
| ALLEN  |     30 | 1600.00 |  1566.666667 |
| WARD   |     30 | 1250.00 |  1566.666667 |
| JONES  |     20 | 2975.00 |  2175.000000 |
| MARTIN |     30 | 1250.00 |  1566.666667 |
| BLAKE  |     30 | 2850.00 |  1566.666667 |
| CLARK  |     10 | 2450.00 |  2916.666667 |
| SCOTT  |     20 | 3000.00 |  2175.000000 |
| KING   |     10 | 5000.00 |  2916.666667 |
| TURNER |     30 | 1500.00 |  1566.666667 |
| ADAMS  |     20 | 1100.00 |  2175.000000 |
| JAMES  |     30 |  950.00 |  1566.666667 |
| FORD   |     20 | 3000.00 |  2175.000000 |
| MILLER |     10 | 1300.00 |  2916.666667 |
+--------+--------+---------+--------------+

​ 现在我们得到的就是一个比较干净的表了,其中每个员工与各自部门的平均工资也都有了,我们只需要用 where 条件进行工资比较即可:

mysql> select ename, tmp.deptno, sal, 平均工资  -> from emp, (select deptno, avg(sal) 平均工资 from emp group by deptno) as tmp-> where emp.deptno<=>tmp.deptno and emp.sal > 平均工资-> ;
+-------+--------+---------+--------------+
| ename | deptno | sal     | 平均工资     |
+-------+--------+---------+--------------+
| ALLEN |     30 | 1600.00 |  1566.666667 |
| JONES |     20 | 2975.00 |  2175.000000 |
| BLAKE |     30 | 2850.00 |  1566.666667 |
| SCOTT |     20 | 3000.00 |  2175.000000 |
| KING  |     10 | 5000.00 |  2916.666667 |
| FORD  |     20 | 3000.00 |  2175.000000 |
+-------+--------+---------+--------------+
6 rows in set (0.00 sec)

​ 这些就是各个部门里的中流砥柱啦!上面唯一 需要注意的就是在使用列字段的时候,有冲突的列字段的话需要用表名指定

​ 最后还有一个工作,就是这些员工的所在位置,因为位置字段在 dept 表中,所以这里我们还得再套一层外壳用于多表查询,这个并不难,只是整个语句就很长罢了,注意不要搞混就行!

mysql> select ename, tmp2.deptno, sal, 平均工资, loc 位置 -> from dept, (select ename, tmp.deptno, sal, 平均工资 from emp, (select deptno, avg(sal) 平均工资 from emp group by deptno) as tmp where emp.deptno<=>tmp.ddeptno and emp.sal>平均工资) as tmp2 -> where tmp2.deptno<=>dept.deptno-> ;
+-------+--------+---------+--------------+----------+
| ename | deptno | sal     | 平均工资     | 位置     |
+-------+--------+---------+--------------+----------+
| ALLEN |     30 | 1600.00 |  1566.666667 | CHICAGO  |
| JONES |     20 | 2975.00 |  2175.000000 | DALLAS   |
| BLAKE |     30 | 2850.00 |  1566.666667 | CHICAGO  |
| SCOTT |     20 | 3000.00 |  2175.000000 | DALLAS   |
| KING  |     10 | 5000.00 |  2916.666667 | NEW YORK |
| FORD  |     20 | 3000.00 |  2175.000000 | DALLAS   |
+-------+--------+---------+--------------+----------+
6 rows in set (0.00 sec)

② 查找每个部门工资最高的人的姓名、工资、部门、最高工资

​ 这个和上一道题其实是差不多的,只不过改成了求最高工资,那么通过多表查询+子查询之后,得到的每个员工的工资与最高工资,我们只需要获取工资等于最高工资的那些员工即可,就是我们要找的部门工资最高的人!

mysql> select ename, sal, tmp.deptno, 最高工资 -> from emp, (select deptno, max(sal) 最高工资 from emp group by deptno) as tmp -> where emp.deptno<=>tmp.deptno and emp.sal <=> 最高工资-> ;
+-------+---------+--------+--------------+
| ename | sal     | deptno | 最高工资     |
+-------+---------+--------+--------------+
| BLAKE | 2850.00 |     30 |      2850.00 |
| SCOTT | 3000.00 |     20 |      3000.00 |
| KING  | 5000.00 |     10 |      5000.00 |
| FORD  | 3000.00 |     20 |      3000.00 |
+-------+---------+--------+--------------+
4 rows in set (0.00 sec)

③ 显示每个部门的信息(部门名、编号、地址)和人员数量

​ 万变不离其宗,这里其实就是要将两张表进行拼接也就是多表查询,然后其中因为人员数量是需要分组聚合统计的,所以要使用子查询进行嵌套,大体的思路还是不变的!

mysql> select dept.deptno, dname, loc, 人员数量-> from dept, (select deptno, count(*) 人员数量 from emp group by deptno) as tmp -> where dept.deptno<=>tmp.deptno-> ;
+--------+------------+----------+--------------+
| deptno | dname      | loc      | 人员数量     |
+--------+------------+----------+--------------+
|     10 | ACCOUNTING | NEW YORK |            3 |
|     20 | RESEARCH   | DALLAS   |            5 |
|     30 | SALES      | CHICAGO  |            6 |
+--------+------------+----------+--------------+
3 rows in set (0.00 sec)

Ⅴ. 合并查询

​ 在实际应用中,为了合并多个 select 的执行结果,可以使用集合操作符 unionunion all,但是要注意的是,它们虽然是合并,但是 并不是我们所说的笛卡尔积,只是对它们的结果进行了合并集合而已!

1、union

​ 该操作符用于取得两个结果集的并集。当使用该操作符时,会自动去掉结果集中的重复行

​ 下面举个例子就懂了!

案例:将工资大于2500或职位是MANAGER的人找出来
mysql> select ename, sal, job from emp where sal > 2500  union -> select ename, sal, job from emp where job<=>'MANAGER';	--去掉了重复记录
+-------+---------+-----------+
| ename | sal     | job       |
+-------+---------+-----------+
| JONES | 2975.00 | MANAGER   |
| BLAKE | 2850.00 | MANAGER   |
| SCOTT | 3000.00 | ANALYST   |
| KING  | 5000.00 | PRESIDENT |
| FORD  | 3000.00 | ANALYST   |
| CLARK | 2450.00 | MANAGER   |
+-------+---------+-----------+
6 rows in set (0.00 sec)

​ 可以看到结果集是 不存在重复的

2、union all

​ 该操作符用于取得两个结果集的并集。当使用该操作符时,不会去掉结果集中的重复行

​ 下面举个例子就懂了!

案例:将工资大于2500或职位是MANAGER的人找出来
mysql> select ename, sal, job from emp where sal > 2500  union all -> select ename, sal, job from emp where job<=>'MANAGER';
+-------+---------+-----------+
| ename | sal     | job       |
+-------+---------+-----------+
| JONES | 2975.00 | MANAGER   |
| BLAKE | 2850.00 | MANAGER   |
| SCOTT | 3000.00 | ANALYST   |
| KING  | 5000.00 | PRESIDENT |
| FORD  | 3000.00 | ANALYST   |
| JONES | 2975.00 | MANAGER   |
| BLAKE | 2850.00 | MANAGER   |
| CLARK | 2450.00 | MANAGER   |
+-------+---------+-----------+
8 rows in set (0.00 sec)

​ 可以看到结果集是 存在重复的

实战OJ

  • 获取所有非manager的员工emp_no
  • 获取所有员工当前的manager

http://www.mrgr.cn/news/82579.html

相关文章:

  • TensorFlow深度学习实战(4)——正则化技术详解
  • 什么是Kafka的重平衡机制?
  • 如何在centos中进行有效的网络管理
  • 若要把普通表转成分区表,就需要先新建分区表,然后把普通表中的数据导入新建分区表。 具体怎么导入?
  • 【Adobe Acrobat PDF】Acrobat failed to connect to a DDE server.是怎么回事?
  • 深入解析-正则表达式
  • leetcode刷题笔记
  • iOS 逆向学习 - iOS Architecture Cocoa Touch Layer
  • 组会 | DenseNet
  • HCIA-Access V2.5_7_3_XG(S)原理_关键技术
  • sql server期末复习
  • 内部类 --- (寄生的哲学)
  • 对计网大题的一些指正(中间介绍一下CDM的原理和应用)
  • springCloud 脚手架项目功能模块:Java分布式锁
  • 对一段已知行情用python中画出K线图~
  • 从零开始RTSP协议的实时流媒体拉流(pull)的设计与实现(一)
  • 《Android最全面试题-Offer直通车》目录
  • WPS表格技巧01-项目管理中的基本功能-计划和每日记录的对应
  • GIS算法基础知识点总结
  • C++11编译器优化以及引用折叠
  • 《计算机网络A》单选题-复习题库解析-3
  • QML使用Popup实现弹出Message
  • VB.NET CRC32 校验
  • 关于游戏销量的思考
  • 默认ip无法访问,利用dhcp功能获取ip进行访问的方法
  • 使用FDBatchMove的几个问题总结