单表查询回顾与练习
注:下面的依旧基于 scott
数据库
MariaDB [scott]> select * from emp;
+--------+--------+-----------+------+---------------------+---------+---------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+--------+-----------+------+---------------------+---------+---------+--------+
| 007369 | SMITH | CLERK | 7902 | 1980-12-17 00:00:00 | 800.00 | NULL | 20 |
| 007499 | ALLEN | SALESMAN | 7698 | 1981-02-20 00:00:00 | 1600.00 | 300.00 | 30 |
| 007521 | WARD | SALESMAN | 7698 | 1981-02-22 00:00:00 | 1250.00 | 500.00 | 30 |
| 007566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL | 20 |
| 007654 | MARTIN | SALESMAN | 7698 | 1981-09-28 00:00:00 | 1250.00 | 1400.00 | 30 |
| 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 |
| 007844 | TURNER | SALESMAN | 7698 | 1981-09-08 00:00:00 | 1500.00 | 0.00 | 30 |
| 007876 | ADAMS | CLERK | 7788 | 1987-05-23 00:00:00 | 1100.00 | NULL | 20 |
| 007900 | JAMES | CLERK | 7698 | 1981-12-03 00:00:00 | 950.00 | NULL | 30 |
| 007902 | FORD | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.00 | NULL | 20 |
| 007934 | MILLER | CLERK | 7782 | 1982-01-23 00:00:00 | 1300.00 | NULL | 10 |
+--------+--------+-----------+------+---------------------+---------+---------+--------+
14 rows in set (0.00 sec)
1.查询工资高于500或岗位为MANAGER的雇员,同时还要满足他们的姓名首字母为大写的J
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.01 sec)
2.按照部门号升序,工资降序排序
select *
from emp
order by deptno, 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 |
| 007902 | FORD | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.00 | NULL | 20 |
| 007788 | SCOTT | ANALYST | 7566 | 1987-04-19 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 |
| 007654 | MARTIN | SALESMAN | 7698 | 1981-09-28 00:00:00 | 1250.00 | 1400.00 | 30 |
| 007521 | WARD | SALESMAN | 7698 | 1981-02-22 00:00:00 | 1250.00 | 500.00 | 30 |
| 007900 | JAMES | CLERK | 7698 | 1981-12-03 00:00:00 | 950.00 | NULL | 30 |
+--------+--------+-----------+------+---------------------+---------+---------+--------+
3.按照年薪降序排序
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.06 sec)
4.显示工资最高的员工的名字和工作岗位
答案一:
使用 order by
降序排序
select ename, job
from emp
order by sal desc
limit 1;
答案二:
子查询
select ename, job
from emp
where sal = (
select max(sal)
from emp
);
查询结果:
+-------+-----------+
| ename | job |
+-------+-----------+
| KING | PRESIDENT |
+-------+-----------+
1 row in set (0.00 sec)
5.显示工资高于平均工资的员工信息
select ename, sal
from emp
where sal > (
select avg(sal)
from emp
);
查询结果:
+-------+---------+
| ename | sal |
+-------+---------+
| JONES | 2975.00 |
| BLAKE | 2850.00 |
| CLARK | 2450.00 |
| SCOTT | 3000.00 |
| KING | 5000.00 |
| FORD | 3000.00 |
+-------+---------+
6 rows in set (0.00 sec)
6.显示每个部门的平均工资和最高工资
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的部门号及其平均工资
select deptno, avg(sal) 平均工资
from emp
group by deptno
having avg(sal) < 2000;
查询结果:
+--------+--------------+
| deptno | 平均工资 |
+--------+--------------+
| 30 | 1566.666667 |
+--------+--------------+
1 row in set (0.00 sec)
8.显示每种岗位的雇员数量和平均工资
select job, count(distinct empno) 雇员数量, 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)
多表查询
其实,from 后面可以跟多张表
我们不妨尝试执行这个语句:
select * from emp, dept;
查询结果:
一共 56 行,而 emp
表是 14 行, dept
表是 4 行。一共 11 列,其中 8 列是 emp
表的,3 列的 dept
表的。
我们发现,这张大表的行数等于两个小表行数的乘积。
其实,这里的大表是两张小表作笛卡尔积的结果,也就是将一张表中的每一条记录都与另一张表中的所有记录进行组合。这本质上就是一种穷举
但是,这张表中有大量记录是没有意义的,比如第一条记录,将部门 20 的 SMITH 和部门 10 的信息组合,没有意义。所以我们需要筛选条件
select *
from emp, dept
where emp.deptno = dept.deptno;
注意:
使用 表名.字段名
来区分不同表中的相同字段
查询结果:
题目:
1.显示雇员名,雇员工资以及所在部门的名字
我们直接从这张组合好的表里面拿就可以了
select ename, sal, dname
from emp, dept
where emp.deptno = dept.deptno;
查询结果:
+--------+---------+------------+
| ename | sal | dname |
+--------+---------+------------+
| SMITH | 800.00 | RESEARCH |
| ALLEN | 1600.00 | SALES |
| WARD | 1250.00 | SALES |
| JONES | 2975.00 | RESEARCH |
| MARTIN | 1250.00 | SALES |
| BLAKE | 2850.00 | SALES |
| CLARK | 2450.00 | ACCOUNTING |
| SCOTT | 3000.00 | RESEARCH |
| KING | 5000.00 | ACCOUNTING |
| TURNER | 1500.00 | SALES |
| ADAMS | 1100.00 | RESEARCH |
| JAMES | 950.00 | SALES |
| FORD | 3000.00 | RESEARCH |
| MILLER | 1300.00 | ACCOUNTING |
+--------+---------+------------+
14 rows in set (0.00 sec)
总结:
多表查询,其实就是先将多表组合成一张表,然后进行单表查询。注意读题时确定需要的数据和哪些表有关。
2.显示部门号为 10 的部门名,员工名和工资
select dname, ename, sal
from emp, dept
where emp.deptno = dept.deptno and emp.deptno = 10;
查询结果:
+------------+--------+---------+
| dname | ename | sal |
+------------+--------+---------+
| ACCOUNTING | CLARK | 2450.00 |
| ACCOUNTING | KING | 5000.00 |
| ACCOUNTING | MILLER | 1300.00 |
+------------+--------+---------+
3 rows in set (0.00 sec)
3.显示各个员工的姓名,工资,工资级别
select ename, sal, grade
from emp, salgrade
where sal between losal and hisal;
查询结果:
+--------+---------+-------+
| ename | sal | grade |
+--------+---------+-------+
| SMITH | 800.00 | 1 |
| ALLEN | 1600.00 | 3 |
| WARD | 1250.00 | 2 |
| JONES | 2975.00 | 4 |
| MARTIN | 1250.00 | 2 |
| BLAKE | 2850.00 | 4 |
| CLARK | 2450.00 | 4 |
| SCOTT | 3000.00 | 4 |
| KING | 5000.00 | 5 |
| TURNER | 1500.00 | 3 |
| ADAMS | 1100.00 | 1 |
| JAMES | 950.00 | 1 |
| FORD | 3000.00 | 4 |
| MILLER | 1300.00 | 2 |
+--------+---------+-------+
14 rows in set (0.00 sec)
自连接
自连接(self-join)是指在一个表中将其不同的行之间建立关联关系的查询操作。即,将一个表自己与自己连接
自连接通常用于需要在同一张表中进行比较和分析的情况,例如查找同一部门中的员工之间的关系、查找某个节点的所有子节点等。
例:
1.显示员工FORD的上级领导的编号和姓名
- 使用子查询
select empno, ename
from emp
where empno = (
select mgr
from emp
where ename = 'FORD'
);
查询结果:
+--------+-------+
| empno | ename |
+--------+-------+
| 007566 | JONES |
+--------+-------+
1 row in set (0.01 sec)
- 自连接
实现自连接的常用方法有两种,一是使用自身表格名和别名,二是使用 INNER JOIN
,这里先用第一种方法。
select m.empno, m.ename
from emp e, emp m
where e.mgr = m.empno and e.ename = 'FORD';
在这个例子中,表格 emp
使用别名 e
和 m
来表示不同的表格,然后通过连接条件 e.mgr = m.empno
来建立关联关系。
查询结果:
+--------+-------+
| empno | ename |
+--------+-------+
| 007566 | JONES |
+--------+-------+
1 row in set (0.00 sec)
注:from emp emp
这种写法是错误的,因为自连接后每个字段都有重名,会导致条件筛选和查询时都无法区分
多行子查询(单列)
我们上面用到的都是单行子查询,即只返回一行结果的子查询
多行子查询(multi-row subquery)是指返回多行结果的子查询。多行子查询通常用于比较子查询结果与某个范围、集合或列表中的值,或者用于在 WHERE
或 HAVING
语句中过滤数据。
in 运算符
in
运算符用于测试某个值是否与指定值集合中的任何一个值相等,如果相等,则返回 true,否则返回 false。该运算符通常与子查询一起使用,以便在外部查询中比较内部查询返回的结果。
例:
1.查询和10号部门的工作岗位相同的雇员的名字,岗位,工资,部门号,但是不包含10号部门自己的
select ename, job, sal, deptno
from emp
where job in (
select distinct job
from emp
where deptno = 10
) and deptno != 10;
查询结果:
+-------+---------+---------+--------+
| ename | job | sal | deptno |
+-------+---------+---------+--------+
| SMITH | CLERK | 800.00 | 20 |
| JONES | MANAGER | 2975.00 | 20 |
| BLAKE | MANAGER | 2850.00 | 30 |
| ADAMS | CLERK | 1100.00 | 20 |
| JAMES | CLERK | 950.00 | 30 |
+-------+---------+---------+--------+
5 rows in set (0.01 sec)
all 关键字
all
关键字,用于指定比较运算符(例如 =
、>
、<
、>=
、<=
等)与子查询返回的所有值进行比较。all
关键字可以用于 where
或 having
语句中,放在子查询的前面,用于筛选出符合指定条件的记录。
使用 all
关键字时,比较运算符将与子查询返回的所有值进行比较,并返回 true
或 false
。如果比较运算符与所有值都匹配,则 all
返回 true
,否则返回 false
。如果子查询返回的结果为空,则 all
返回 true
。
例:
显示工资比部门30的所有员工的工资高的员工的姓名,工资,部门号
- 使用
all
关键字
select ename, sal, deptno
from emp
where sal > all (
select distinct 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)
- 使用
max()
比10部门所有人工资高,就是比10部门最高工资高
使用 max 聚合函数,转换为单行子查询
select ename, sal, deptno
from emp
where sal > (
select max(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)
any 关键字
与 all
关键字的区别:
all
关键字表示与子查询中所有值比较,全为 true
返回 true
,否则返回 false
any
关键字表示与子查询中所有值比较,有 true
则返回 true
,全为 false
返回 false
例:
显示工资比部门30的任意员工的工资高的员工的姓名,工资,部门号(包含部门30的员工)
- 使用
any
select ename, sal, deptno
from emp
where sal > any (
select distinct 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.00 sec)
- 使用 min
select ename, sal, deptno
from emp
where sal > (
select min(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.00 sec)
多列子查询
我们已经学会了单行单列和多行单列的子查询。
多列子查询(也称为多值子查询)是指返回多个列作为结果的子查询,这些值通常是作为一个整体被传递给外部查询。多列子查询可以在 SELECT
、WHERE
、FROM
和 HAVING
语句中使用,用于返回多个值以进行进一步处理或过滤。
例:
查询和SMITH的部门和岗位完全相同的所有雇员,不含SMITH本人
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)
注意:
由于多列子查询返回的是多个值,所以需要将这些值用括号括起来,以便在 WHERE 子句中使用。
from 子句中的子查询
在 form
子句中使用子查询可以帮助我们从嵌套查询的结果集中检索数据,并将其用作外部查询的表格或视图。这对于需要根据特定的条件筛选或连接数据的查询非常有用。
例:
1.显示每个高于自己部门平均工资的员工姓名、部门、工资、平均工资
select ename, e.deptno, sal, avgsal
from emp e, (
select deptno, avg(sal) avgsal
from emp
group by deptno
) s
where e.deptno = s.deptno and sal > avgsal;
上述查询语句先用子查询查询各部门的平均工资,然后与 emp 表连接。
查询结果:
+-------+--------+---------+-------------+
| ename | deptno | sal | avgsal |
+-------+--------+---------+-------------+
| 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)
2.查找每个部门工资最高的人的姓名、工资、部门、最高工资
select ename, sal, s.deptno, msal
from emp e, (
select deptno, max(sal) msal
from emp
group by deptno
) s
where e.deptno = s.deptno and sal = msal;
查询结果:
+-------+---------+--------+---------+
| ename | sal | deptno | msal |
+-------+---------+--------+---------+
| 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)
3.显示每个部门的信息(部门名,编号,地址)和人员数量
- 使用子查询
select dname, tmp.deptno, loc, dept_size
from dept, (
select deptno, count(*) dept_size
from emp
group by deptno
) tmp
where dept.deptno = tmp.deptno;
查询结果:
+------------+--------+----------+-----------+
| dname | deptno | loc | dept_size |
+------------+--------+----------+-----------+
| ACCOUNTING | 10 | NEW YORK | 3 |
| RESEARCH | 20 | DALLAS | 5 |
| SALES | 30 | CHICAGO | 6 |
+------------+--------+----------+-----------+
3 rows in set (0.00 sec)
- 不使用子查询
select dname, emp.deptno, loc, count(*) dept_size
from dept, emp
where dept.deptno = emp.deptno
group by emp.deptno, dname, loc;
上述查询中 group by
后面的 dname, loc
已经没有实际意义,只是为了这两个字段可以在查询结果中显示
查询结果:
+------------+--------+----------+-----------+
| dname | deptno | loc | dept_size |
+------------+--------+----------+-----------+
| ACCOUNTING | 10 | NEW YORK | 3 |
| RESEARCH | 20 | DALLAS | 5 |
| SALES | 30 | CHICAGO | 6 |
+------------+--------+----------+-----------+
3 rows in set (0.00 sec)
合并查询
合并查询(Union Query)是指将两个或多个 SELECT 查询的结果合并成一个结果集的操作。合并查询的结果集中不包含重复的行。
在 SQL 中,使用 UNION 关键字来执行合并查询。UNION 可以连接两个 SELECT 查询的结果集,返回一个包含两个结果集的集合,并且不包含重复行。如果需要包含重复的行,则可以使用 UNION ALL 关键字。
例:
将工资大于2500或职位是MANAGER的人找出来
- 使用
union
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)
- 使用
or
select ename, sal, job
from emp
where sal > 2500 or job = 'MANAGER';
查询结果:
+-------+---------+-----------+
| ename | sal | job |
+-------+---------+-----------+
| JONES | 2975.00 | MANAGER |
| BLAKE | 2850.00 | MANAGER |
| CLARK | 2450.00 | MANAGER |
| SCOTT | 3000.00 | ANALYST |
| KING | 5000.00 | PRESIDENT |
| FORD | 3000.00 | ANALYST |
+-------+---------+-----------+
6 rows in set (0.00 sec)
注意:
在进行合并查询时,每个 SELECT 查询必须包含相同数量的列,并且这些列必须具有相同的数据类型和顺序。否则,合并查询可能会失败或返回不正确的结果。