Bootstrap

MySQL8 常用命令之DQL(多表联查)

DQL:(Data Query Language)数据查询语言

单表查询

MySQL8 常用命令之DQL(单表查询)

一、去除重复记录

使用distinct关键字

select distinct 字段名 from表名:

  • distinct 只对查询结果进行去重,原表数据不会被修改。
  • distinct 后面可以跟多个字段,表示多个字段联合去重。
mysql> select distinct job,deptno from emp order by deptno;
+-----------+--------+
| job       | deptno |
+-----------+--------+
| CLERK     |     10 |
| MANAGER   |     10 |
| PRESIDENT |     10 |
| ANALYST   |     20 |
| CLERK     |     20 |
| MANAGER   |     20 |
| CLERK     |     30 |
| MANAGER   |     30 |
| SALESMAN  |     30 |
+-----------+--------+
9 rows in set (0.01 sec)

二、连接查询

1. 笛卡尔积现象

当两张表进行连接查询时,如果没有任何条件的限制会发生笛卡尔积现象。如emp表有14条记录,dept表有4条记录,不加任何条件直接在两表查询最终会显示56条数据。

mysql> select ename,dname from emp,dept;
+--------+------------+
| ename  | dname      |
+--------+------------+
| SMITH  | OPERATIONS |
| SMITH  | SALES      |
| SMITH  | RESEARCH   |
| SMITH  | ACCOUNTING |
| ALLEN  | OPERATIONS |
......
| MILLER | RESEARCH   |
| MILLER | ACCOUNTING |
+--------+------------+
56 rows in set (0.00 sec)

可以通过添加条件避免笛卡尔积现象的发生。

mysql> select e.ename,d.dname from emp e,dept d where e.deptno=d.deptno;
+--------+------------+
| ename  | dname      |
+--------+------------+
| SMITH  | RESEARCH   |
| ALLEN  | SALES      |
| WARD   | SALES      |
......
| JAMES  | SALES      |
| FORD   | RESEARCH   |
| MILLER | ACCOUNTING |
+--------+------------+
14 rows in set (0.01 sec)
2. 内连接
select ... from A inner join B on A和B的连接条件 where 筛选条件;

说明:

  • inner可以省略,带着inner可读性更好
  • 内连接的特点:AB两表之间没有主次关系,将完全能够满足连接条件的数据查询出来。

2.1 等值连接
on 后面的连接条件是等量关系。

案例:查询员工的部门名称。

mysql> select e.ename,d.dname from emp e inner join dept d on e.deptno=d.deptno;
+--------+------------+
| ename  | dname      |
+--------+------------+
| SMITH  | RESEARCH   |
| ALLEN  | SALES      |
| WARD   | SALES      |
......
| JAMES  | SALES      |
| FORD   | RESEARCH   |
| MILLER | ACCOUNTING |
+--------+------------+
14 rows in set (0.01 sec)

2.2 非等值连接
连接条件不是等量关系。

案例:查询每个员工的薪资等级。

mysql> select e.ename,e.sal,s.grade from emp e 
	 > join salgrade s on e.sal between s.losal and s.hisal;
+--------+---------+-------+
| ename  | sal     | grade |
+--------+---------+-------+
| SMITH  |  800.00 |     1 |
| ALLEN  | 1600.00 |     3 |
| WARD   | 1250.00 |     2 |
| JONES  | 2975.00 |     4 |
......
| FORD   | 3000.00 |     4 |
| MILLER | 1300.00 |     2 |
+--------+---------+-------+
14 rows in set (0.01 sec)

2.3 自连接
将一张表看做是两张表。

案例:查询员工的上级领导并显示。

mysql> select e.ename,m.ename from 
	 > (select empno,ename from emp) as m 
	 > join emp e on e.mgr=m.empno;
+--------+-------+
| ename  | ename |
+--------+-------+
| SMITH  | FORD  |
| ALLEN  | BLAKE |
| WARD   | BLAKE |
| JONES  | KING  |
| MARTIN | BLAKE |
| BLAKE  | KING  |
| CLARK  | KING  |
| SCOTT  | JONES |
| TURNER | BLAKE |
| ADAMS  | SCOTT |
| JAMES  | BLAKE |
| FORD   | JONES |
| MILLER | CLARK |
+--------+-------+
13 rows in set (0.00 sec)
3. 外连接

两张表具有主次关系。语句中的outer可以省略。
3.1 左外连接

select ... from A left outer join B on 连接条件; 

说明:将join关键字左边的表看做是主表,左表中的数据将被全部查询出来,右表中不满足条件的数据使用NULL填充。

mysql> select e.ename,d.dname from dept d 
	 > left join emp e on e.deptno=d.deptno;
+--------+------------+
| ename  | dname      |
+--------+------------+
| MILLER | ACCOUNTING |
| KING   | ACCOUNTING |
| CLARK  | ACCOUNTING |
| FORD   | RESEARCH   |
| ADAMS  | RESEARCH   |
| SCOTT  | RESEARCH   |
| JONES  | RESEARCH   |
| SMITH  | RESEARCH   |
| JAMES  | SALES      |
| TURNER | SALES      |
| BLAKE  | SALES      |
| MARTIN | SALES      |
| WARD   | SALES      |
| ALLEN  | SALES      |
| NULL   | OPERATIONS |
+--------+------------+
15 rows in set (0.00 sec)

3.2 右外连接

select ... from A right outer join B on 连接条件; 

说明:将join关键字右边的表看做是主表,右表中的数据将被全部查询出来,左表中不满足条件的数据使用NULL填充。

mysql> select e.ename,d.dname from emp e 
	 > right join dept d on e.deptno=d.deptno;
+--------+------------+
| ename  | dname      |
+--------+------------+
| MILLER | ACCOUNTING |
| KING   | ACCOUNTING |
| CLARK  | ACCOUNTING |
| FORD   | RESEARCH   |
| ADAMS  | RESEARCH   |
| SCOTT  | RESEARCH   |
| JONES  | RESEARCH   |
| SMITH  | RESEARCH   |
| JAMES  | SALES      |
| TURNER | SALES      |
| BLAKE  | SALES      |
| MARTIN | SALES      |
| WARD   | SALES      |
| ALLEN  | SALES      |
| NULL   | OPERATIONS |
+--------+------------+
15 rows in set (0.01 sec)

案例:查询所有员工(包括KING)的上级领导并显示员工名和领导名。

mysql> select e.ename,m.ename from emp e
    -> left join (select empno,ename from emp) m
    -> on e.mgr=m.empno;
+--------+-------+
| ename  | ename |
+--------+-------+
| SMITH  | FORD  |
| ALLEN  | BLAKE |
| WARD   | BLAKE |
| JONES  | KING  |
| MARTIN | BLAKE |
| BLAKE  | KING  |
| CLARK  | KING  |
| SCOTT  | JONES |
| KING   | NULL  |
| TURNER | BLAKE |
| ADAMS  | SCOTT |
| JAMES  | BLAKE |
| FORD   | JONES |
| MILLER | CLARK |
+--------+-------+
14 rows in set (0.00 sec)
4.三表联查,四表联查…
select ... from A join B on AB连接条件 join C on AC连接条件 right join D on AD连接条件...;

案例1:查询每个员工的部门名称以及工资等级,要求显示员工名、部门名、薪资、薪资等级。

mysql> select e.ename,d.dname,e.sal,s.grade from emp e
    -> join dept d on e.deptno=d.deptno
    -> join salgrade s on e.sal between s.losal and s.hisal;
+--------+------------+---------+-------+
| ename  | dname      | sal     | grade |
+--------+------------+---------+-------+
| SMITH  | RESEARCH   |  800.00 |     1 |
| ALLEN  | SALES      | 1600.00 |     3 |
| WARD   | SALES      | 1250.00 |     2 |
| JONES  | RESEARCH   | 2975.00 |     4 |
| MARTIN | SALES      | 1250.00 |     2 |
| BLAKE  | SALES      | 2850.00 |     4 |
| CLARK  | ACCOUNTING | 2450.00 |     4 |
| SCOTT  | RESEARCH   | 3000.00 |     4 |
| KING   | ACCOUNTING | 5000.00 |     5 |
| TURNER | SALES      | 1500.00 |     3 |
| ADAMS  | RESEARCH   | 1100.00 |     1 |
| JAMES  | SALES      |  950.00 |     1 |
| FORD   | RESEARCH   | 3000.00 |     4 |
| MILLER | ACCOUNTING | 1300.00 |     2 |
+--------+------------+---------+-------+
14 rows in set (0.01 sec)

案例2: 查询每个员工的领导名,部门名称,工资以及工资等级。

mysql> select e.ename,m.ename mname,d.dname,e.sal,s.grade from emp e
    -> left join (select empno,ename from emp) as m on e.mgr=m.empno
    -> join dept d on e.deptno=d.deptno
    -> join salgrade s on e.sal between s.losal and s.hisal;
+--------+-------+------------+---------+-------+
| ename  | mname | dname      | sal     | grade |
+--------+-------+------------+---------+-------+
| SMITH  | FORD  | RESEARCH   |  800.00 |     1 |
| ALLEN  | BLAKE | SALES      | 1600.00 |     3 |
| WARD   | BLAKE | SALES      | 1250.00 |     2 |
| JONES  | KING  | RESEARCH   | 2975.00 |     4 |
| MARTIN | BLAKE | SALES      | 1250.00 |     2 |
| BLAKE  | KING  | SALES      | 2850.00 |     4 |
| CLARK  | KING  | ACCOUNTING | 2450.00 |     4 |
| SCOTT  | JONES | RESEARCH   | 3000.00 |     4 |
| KING   | NULL  | ACCOUNTING | 5000.00 |     5 |
| TURNER | BLAKE | SALES      | 1500.00 |     3 |
| ADAMS  | SCOTT | RESEARCH   | 1100.00 |     1 |
| JAMES  | BLAKE | SALES      |  950.00 |     1 |
| FORD   | JONES | RESEARCH   | 3000.00 |     4 |
| MILLER | CLARK | ACCOUNTING | 1300.00 |     2 |
+--------+-------+------------+---------+-------+
14 rows in set (0.00 sec)

三、 子查询

select语句中嵌套select语句,称为子查询。

1. where中的子查询

上一篇文章中提到where子句中不能直接使用分组函数,可以使用子查询解决这个问题。

案例: 查询比最低工资高的员工姓名和工资。

mysql> select ename,sal from emp
    -> where sal>(select min(sal) from emp);
+--------+---------+
| ename  | sal     |
+--------+---------+
| ALLEN  | 1600.00 |
| WARD   | 1250.00 |
| JONES  | 2975.00 |
| MARTIN | 1250.00 |
| BLAKE  | 2850.00 |
| CLARK  | 2450.00 |
| SCOTT  | 3000.00 |
| KING   | 5000.00 |
| TURNER | 1500.00 |
| ADAMS  | 1100.00 |
| JAMES  |  950.00 |
| FORD   | 3000.00 |
| MILLER | 1300.00 |
+--------+---------+
13 rows in set (0.01 sec)
2. from后的子查询

:from后面的子查询,可以将子查询结果当作一张临时表。

案例:查询每个岗位的平均工资的薪资等级。

mysql> select a.job,a.avgsal,s.grade from
    -> (select job,round(avg(sal),2) avgsal from emp group by job) a
    -> join salgrade s on a.avgsal between s.losal and s.hisal;
+-----------+---------+-------+
| job       | avgsal  | grade |
+-----------+---------+-------+
| CLERK     |  1037.5 |     1 |
| SALESMAN  |    1400 |     2 |
| MANAGER   | 2758.33 |     4 |
| ANALYST   |    3000 |     4 |
| PRESIDENT |    5000 |     5 |
+-----------+---------+-------+
5 rows in set (0.00 sec)
3. select后的子查询(了解)

案例:查询员工的所属部门。

mysql> select e.ename,(select d.dname from dept d where e.deptno=d.deptno) dname from emp e;
+--------+------------+
| ename  | dname      |
+--------+------------+
| SMITH  | RESEARCH   |
| ALLEN  | SALES      |
| WARD   | SALES      |
| JONES  | RESEARCH   |
| MARTIN | SALES      |
| BLAKE  | SALES      |
| CLARK  | ACCOUNTING |
| SCOTT  | RESEARCH   |
| KING   | ACCOUNTING |
| TURNER | SALES      |
| ADAMS  | RESEARCH   |
| JAMES  | SALES      |
| FORD   | RESEARCH   |
| MILLER | ACCOUNTING |
+--------+------------+
14 rows in set (0.00 sec)

:对于select后的子查询来说,一次返回一条数据,多于一条会报错。

四、union 合并查询结果

有ABC三张表,各有10条记录
A 连接 B 连接 C ----- 匹配次数:10*10*10 = 1000次
------------------------------------------------------------------
A 连接 B ----- 匹配次数: 10*10 = 100次
A 连接 C ----- 匹配次数: 10*10 = 100次
使用union合并:100+100 = 200次

案例: 查询工作岗位是MANAGER和SALESMAN的员工。

mysql> select ename,job from emp where job='manager' or job='salesman';
mysql> select ename,job from emp where job in ('manager','salesman');

以上两条语句是利用之前提到的知识写出来的,结果相同。

mysql> select ename,job from emp where job='manager'
    -> union
    -> select ename,job from emp where job='salesman';

注:union在进行结果合并时,要求两个结果集的列数相同。在书写语句时查询字段顺序书写要保持一致。

union的查询效率更高,对于表连接来说,每连接一次新表,匹配的次数满足笛卡尔积,也就是说查询次数会成倍增长,但是union会减少查询次数,同时将查询结果集进行拼接输出。

五、 分页查询

1. limit(start,len)

说明:

  • 将查询结果的一部分取出来。
  • start:起始下标,可以省略,默认从0开始。
  • len:截取长度。
  • mysql中limit在order by后面执行

案例: 查询工资排名前3-5的员工。

mysql> select ename,sal from emp order by sal desc limit 2,3;
+-------+---------+
| ename | sal     |
+-------+---------+
| FORD  | 3000.00 |
| JONES | 2975.00 |
| BLAKE | 2850.00 |
+-------+---------+
3 rows in set (0.00 sec)
2. 通用分页
每页显示pageSize条记录,第pageNo页
公式:limit (pageNo-1) * pageSize,pageSize

DQL总结

1. 查询语句书写顺序
select ...from ...
where ...
group by ... having ...
order by ... limit...
2. 执行顺序
  1. from
  2. where
  3. group by
  4. having
  5. select
  6. order by
  7. limit
;