MySQL03-(进阶篇)

连接查询和子查询

主要讲 :连接查询 , 子查询 union limit

关于查询结果去重

没有去重之前:

mysql> select job from emp;
+-----------+
| job       |
+-----------+
| CLERK     |
| SALESMAN  |
| SALESMAN  |
| MANAGER   |
| SALESMAN  |
| MANAGER   |
| MANAGER   |
| ANALYST   |
| PRESIDENT |
| SALESMAN  |
| CLERK     |
| CLERK     |
| ANALYST   |
| CLERK     |
+-----------+
14 rows in set (0.00 sec)

在字段前面加上distinct关键字:

distinct只能出现在所有字段的最前端,distinct 字段 ,字段… 表示联合去重

mysql> select distinct job from emp;
+-----------+
| job       |
+-----------+
| CLERK     |
| SALESMAN  |
| MANAGER   |
| ANALYST   |
| PRESIDENT |
+-----------+
5 rows in set (0.00 sec)

1.连接查询概述

1.什么是连接查询?

在实际开发中,大部分的情况下都不是从单表中查询数据,一般都是从多张表联合查询取出最终结果

2.连接查询的分类
  • 内连接
    • 等值连接
    • 非等值连接
    • 自连接
  • 外连接
    • 左外连接
    • 右外连接
  • 全连接(了解)
3.笛卡尔积现象

当两张表进行连接查询的时候,没有任何条件限制,最终的查询结果条数是两张表记录条数的乘积

案例:要求显示每个员工,和对应的部门名字

需要从这两张表中查询

emp表,共14条记录

mysql> select * from emp;
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME  | JOB       | MGR  | HIREDATE   | SAL     | COMM    | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
|  7369 | SMITH  | CLERK     | 7902 | 1980-12-17 |  800.00 |    NULL |     20 |
|  7499 | ALLEN  | SALESMAN  | 7698 | 1981-02-20 | 1600.00 |  300.00 |     30 |
|  7521 | WARD   | SALESMAN  | 7698 | 1981-02-22 | 1250.00 |  500.00 |     30 |
|  7566 | JONES  | MANAGER   | 7839 | 1981-04-02 | 2975.00 |    NULL |     20 |
|  7654 | MARTIN | SALESMAN  | 7698 | 1981-09-28 | 1250.00 | 1400.00 |     30 |
|  7698 | BLAKE  | MANAGER   | 7839 | 1981-05-01 | 2850.00 |    NULL |     30 |
|  7782 | CLARK  | MANAGER   | 7839 | 1981-06-09 | 2450.00 |    NULL |     10 |
|  7788 | SCOTT  | ANALYST   | 7566 | 1987-04-19 | 3000.00 |    NULL |     20 |
|  7839 | KING   | PRESIDENT | NULL | 1981-11-17 | 5000.00 |    NULL |     10 |
|  7844 | TURNER | SALESMAN  | 7698 | 1981-09-08 | 1500.00 |    0.00 |     30 |
|  7876 | ADAMS  | CLERK     | 7788 | 1987-05-23 | 1100.00 |    NULL |     20 |
|  7900 | JAMES  | CLERK     | 7698 | 1981-12-03 |  950.00 |    NULL |     30 |
|  7902 | FORD   | ANALYST   | 7566 | 1981-12-03 | 3000.00 |    NULL |     20 |
|  7934 | MILLER | CLERK     | 7782 | 1982-01-23 | 1300.00 |    NULL |     10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
14 rows in set (0.00 sec)

dept表,共4条记录

mysql> select * from dept;
+--------+------------+----------+
| DEPTNO | DNAME      | LOC      |
+--------+------------+----------+
|     10 | ACCOUNTING | NEW YORK |
|     20 | RESEARCH   | DALLAS   |
|     30 | SALES      | CHICAGO  |
|     40 | OPERATIONS | BOSTON   |
+--------+------------+----------+
4 rows in set (0.00 sec)

当执行select e.ename,d.dname from emp e,dept d时候会出现56条记录,这就是笛卡儿积现象

mysql> select e.ename,d.dname from emp e,dept d;
+--------+------------+
| ename  | dname      |
+--------+------------+
| SMITH  | ACCOUNTING |
| SMITH  | RESEARCH   |
| SMITH  | SALES      |
| SMITH  | OPERATIONS |
| ALLEN  | ACCOUNTING |
| ALLEN  | RESEARCH   |
| ALLEN  | SALES      |
| ALLEN  | OPERATIONS |
| WARD   | ACCOUNTING |
| WARD   | RESEARCH   |
| WARD   | SALES      |
| WARD   | OPERATIONS |
| JONES  | ACCOUNTING |
...
56 rows in set (0.00 sec)
4.避免迪卡尔现象,就是给查询加限定条件
mysql> select
    ->          e.ename,d.dname
    -> from
    ->          emp e,dept d
    -> where
    ->          e.deptno=d.deptno;
    #注意:现在使用的是SQL92的语法,一般不用,只是用做演示
+--------+------------+
| ename  | dname      |
+--------+------------+
| CLARK  | ACCOUNTING |
| KING   | ACCOUNTING |
| MILLER | ACCOUNTING |
| SMITH  | RESEARCH   |
| JONES  | RESEARCH   |
| SCOTT  | RESEARCH   |
| ADAMS  | RESEARCH   |
| FORD   | RESEARCH   |
| ALLEN  | SALES      |
| WARD   | SALES      |
| MARTIN | SALES      |
| BLAKE  | SALES      |
| TURNER | SALES      |
| JAMES  | SALES      |
+--------+------------+
14 rows in set (0.00 sec)

避免了迪卡尔现象会减少记录的匹配次数吗?

不会,次数还是56次,只是显示的是有效记录

5.关于表的别名
select e.ename,d.dname from emp e,dept d;

好处:

​ 执行效率高

​ 可读性号

2.内连接

等值连接

还是上面的案例:要求显示每个员工,和对应的部门名字

这次我们使用SQL99的语法

select
		e.ename,d.dname 
from
		emp e 
join
 		dept d 
on 
		e.deptno=d.deptno;

输出:

mysql> select e.ename,d.dname from emp e join dept d on e.deptno=d.deptno;
+--------+------------+
| ename  | dname      |
+--------+------------+
| CLARK  | ACCOUNTING |
| KING   | ACCOUNTING |
| MILLER | ACCOUNTING |
| SMITH  | RESEARCH   |
| JONES  | RESEARCH   |
| SCOTT  | RESEARCH   |
| ADAMS  | RESEARCH   |
| FORD   | RESEARCH   |
| ALLEN  | SALES      |
| WARD   | SALES      |
| MARTIN | SALES      |
| BLAKE  | SALES      |
| TURNER | SALES      |
| JAMES  | SALES      |
+--------+------------+

语法

..
A
join
B
on
   连接条件
where
    过滤条件

SQL99的语法更清晰:表的连接条件和后来的where条件分离了

非等值连接

案例:要求显示每个员工名,员工工资,员工工资等级

工资等级表salgrade


+-------+-------+-------+
| GRADE | LOSAL | HISAL |
+-------+-------+-------+
|     1 |   700 |  1200 |
|     2 |  1201 |  1400 |
|     3 |  1401 |  2000 |
|     4 |  2001 |  3000 |
|     5 |  3001 |  9999 |
+-------+-------+-------+
select 
      e.ename,e.sal,s.grade 
from
      emp e 
join
      salgrade s 
on
      e.sal between s.Losal and s.hisal;

输出:

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 |
| 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)
自连接

最大的特点是:一张表当成两张表

案例:要求显示员工名字,和对应领导的名字

注意:领导也是员工,存在同一张表

mysql> select * from emp;
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME  | JOB       | MGR  | HIREDATE   | SAL     | COMM    | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
|  7369 | SMITH  | CLERK     | 7902 | 1980-12-17 |  800.00 |    NULL |     20 |
|  7499 | ALLEN  | SALESMAN  | 7698 | 1981-02-20 | 1600.00 |  300.00 |     30 |
|  7521 | WARD   | SALESMAN  | 7698 | 1981-02-22 | 1250.00 |  500.00 |     30 |
|  7566 | JONES  | MANAGER   | 7839 | 1981-04-02 | 2975.00 |    NULL |     20 |
|  7654 | MARTIN | SALESMAN  | 7698 | 1981-09-28 | 1250.00 | 1400.00 |     30 |
|  7698 | BLAKE  | MANAGER   | 7839 | 1981-05-01 | 2850.00 |    NULL |     30 |
|  7782 | CLARK  | MANAGER   | 7839 | 1981-06-09 | 2450.00 |    NULL |     10 |
|  7788 | SCOTT  | ANALYST   | 7566 | 1987-04-19 | 3000.00 |    NULL |     20 |
|  7839 | KING   | PRESIDENT | NULL | 1981-11-17 | 5000.00 |    NULL |     10 |
|  7844 | TURNER | SALESMAN  | 7698 | 1981-09-08 | 1500.00 |    0.00 |     30 |
|  7876 | ADAMS  | CLERK     | 7788 | 1987-05-23 | 1100.00 |    NULL |     20 |
|  7900 | JAMES  | CLERK     | 7698 | 1981-12-03 |  950.00 |    NULL |     30 |
|  7902 | FORD   | ANALYST   | 7566 | 1981-12-03 | 3000.00 |    NULL |     20 |
|  7934 | MILLER | CLERK     | 7782 | 1982-01-23 | 1300.00 |    NULL |     10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
 select
		e.ename as "员工",m.ename as "领导" 
 from 
 		emp e 
 join 
		emp m 
 on
		e.mgr=m.empno;

输出:

+--------+-------+
| 员工    | 领导  |
+--------+-------+
| 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)

里面没有KING,原因是KING没有上级领导

外连接

外连接与内连接有什么区别?

内连接:

​ 假设A和B进行连接,使用内连接的话,凡是A表和B表能够匹配上的记录查询出来,这就是内连接。

外连接:

​ 假设A和B表进行连接,使用外连接的话,AB两张表中有一张是主表一张是副表,主要查询主表中的数据,同时查询副标,当附表中的数据没有和主表中的数据匹配上,附表自动模拟出NULL与之匹配。

  • 左连接:以左表为主表
  • 右连接:以右表为附表

解决上面案例的问题,查询结果没有KING,并不是我们想要的结果,

如果在join 加上left/right就表示左连接/右连接,以左/右表为主

 select e.ename as "员工",m.ename as "领导" from emp e left join emp m on e.mgr=m.empno;

输出结果:

+--------+-------+
| 员工      | 领导    |
+--------+-------+
| 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 |
+--------+-------+

出现了KING,如果换成right,就是以领导表为主

 select e.ename as "员工",m.ename as "领导" from emp e right join emp m on e.mgr=m.empno;
+--------+--------+
| 员工      | 领导     |
+--------+--------+
| NULL   | SMITH  |
| NULL   | ALLEN  |
| NULL   | WARD   |
| SCOTT  | JONES  |
| FORD   | JONES  |
| NULL   | MARTIN |
| ALLEN  | BLAKE  |
| WARD   | BLAKE  |
| MARTIN | BLAKE  |
| TURNER | BLAKE  |
| JAMES  | BLAKE  |
| MILLER | CLARK  |
| ADAMS  | SCOTT  |
| JONES  | KING   |
| BLAKE  | KING   |
| CLARK  | KING   |
| NULL   | TURNER |
| NULL   | ADAMS  |
| NULL   | JAMES  |
| SMITH  | FORD   |
| NULL   | MILLER |
+--------+--------+

案例:找出那个部门没有员工?

分析:肯定是以部门表为主表

先全部查出来

mysql>  select e.* ,d.* from dept d left join emp e on d.deptno=e.deptno;
+-------+--------+-----------+------+------------+---------+---------+--------+--------+------------+----------+
| EMPNO | ENAME  | JOB       | MGR  | HIREDATE   | SAL     | COMM    | DEPTNO | DEPTNO | DNAME      | LOC      |
+-------+--------+-----------+------+------------+---------+---------+--------+--------+------------+----------+
|  7782 | CLARK  | MANAGER   | 7839 | 1981-06-09 | 2450.00 |    NULL |     10 |     10 | ACCOUNTING | NEW YORK |
|  7839 | KING   | PRESIDENT | NULL | 1981-11-17 | 5000.00 |    NULL |     10 |     10 | ACCOUNTING | NEW YORK |
|  7934 | MILLER | CLERK     | 7782 | 1982-01-23 | 1300.00 |    NULL |     10 |     10 | ACCOUNTING | NEW YORK |
|  7369 | SMITH  | CLERK     | 7902 | 1980-12-17 |  800.00 |    NULL |     20 |     20 | RESEARCH   | DALLAS   |
|  7566 | JONES  | MANAGER   | 7839 | 1981-04-02 | 2975.00 |    NULL |     20 |     20 | RESEARCH   | DALLAS   |
|  7788 | SCOTT  | ANALYST   | 7566 | 1987-04-19 | 3000.00 |    NULL |     20 |     20 | RESEARCH   | DALLAS   |
|  7876 | ADAMS  | CLERK     | 7788 | 1987-05-23 | 1100.00 |    NULL |     20 |     20 | RESEARCH   | DALLAS   |
|  7902 | FORD   | ANALYST   | 7566 | 1981-12-03 | 3000.00 |    NULL |     20 |     20 | RESEARCH   | DALLAS   |
|  7499 | ALLEN  | SALESMAN  | 7698 | 1981-02-20 | 1600.00 |  300.00 |     30 |     30 | SALES      | CHICAGO  |
|  7521 | WARD   | SALESMAN  | 7698 | 1981-02-22 | 1250.00 |  500.00 |     30 |     30 | SALES      | CHICAGO  |
|  7654 | MARTIN | SALESMAN  | 7698 | 1981-09-28 | 1250.00 | 1400.00 |     30 |     30 | SALES      | CHICAGO  |
|  7698 | BLAKE  | MANAGER   | 7839 | 1981-05-01 | 2850.00 |    NULL |     30 |     30 | SALES      | CHICAGO  |
|  7844 | TURNER | SALESMAN  | 7698 | 1981-09-08 | 1500.00 |    0.00 |     30 |     30 | SALES      | CHICAGO  |
|  7900 | JAMES  | CLERK     | 7698 | 1981-12-03 |  950.00 |    NULL |     30 |     30 | SALES      | CHICAGO  |
|  NULL | NULL   | NULL      | NULL | NULL       |    NULL |    NULL |   NULL |     40 | OPERATIONS | BOSTON   |
+-------+--------+-----------+------+------------+---------+---------+--------+--------+------------+----------+
15 rows in set (0.00 sec)

可以看到最后一个是我们想要的结果

where筛选出来

mysql>   select d.* from dept d left join emp e on d.deptno=e.deptno where  e.deptno is null;
+--------+------------+--------+
| DEPTNO | DNAME      | LOC    |
+--------+------------+--------+
|     40 | OPERATIONS | BOSTON |
+--------+------------+--------+
1 row in set (0.00 sec)
三张表以及三张表以上的连接

语法结构

...
from
A
join
 B
 on
 连接条件
join
 c
 on
 连接条件
 ...
 

表示A先与B表连接,然后再和C表连接

案例:显示每个员工的名字,部门名称,工资等级

 select
 		e.ename,d.dname,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      | grade |
+--------+------------+-------+
| SMITH  | RESEARCH   |     1 |
| ALLEN  | SALES      |     3 |
| WARD   | SALES      |     2 |
| JONES  | RESEARCH   |     4 |
| MARTIN | SALES      |     2 |
| BLAKE  | SALES      |     4 |
| CLARK  | ACCOUNTING |     4 |
| SCOTT  | RESEARCH   |     4 |
| KING   | ACCOUNTING |     5 |
| TURNER | SALES      |     3 |
| ADAMS  | RESEARCH   |     1 |
| JAMES  | SALES      |     1 |
| FORD   | RESEARCH   |     4 |
| MILLER | ACCOUNTING |     2 |
+--------+------------+-------+
14 rows in set (0.00 sec)

案例:显示每个员工的名字,部门名称,工资等级,以及领导名字:

分析:加入外连接,以emp表为主表

 select 
 		e.ename,d.dname,s.grade,m.ename as "manager" 
 from 
 		emp e join dept d on e.deptno=d.deptno 
 join 
		salgrade s 
 on 
 		e.sal between s.losal and s.hisal 
 left join 
		emp m 
 on
 		e.mgr=m.empno; 
+--------+------------+-------+---------+
| ename  | dname      | grade | manager |
+--------+------------+-------+---------+
| SMITH  | RESEARCH   |     1 | FORD    |
| ALLEN  | SALES      |     3 | BLAKE   |
| WARD   | SALES      |     2 | BLAKE   |
| JONES  | RESEARCH   |     4 | KING    |
| MARTIN | SALES      |     2 | BLAKE   |
| BLAKE  | SALES      |     4 | KING    |
| CLARK  | ACCOUNTING |     4 | KING    |
| SCOTT  | RESEARCH   |     4 | JONES   |
| KING   | ACCOUNTING |     5 | NULL    |
| TURNER | SALES      |     3 | BLAKE   |
| ADAMS  | RESEARCH   |     1 | SCOTT   |
| JAMES  | SALES      |     1 | BLAKE   |
| FORD   | RESEARCH   |     4 | JONES   |
| MILLER | ACCOUNTING |     2 | CLARK   |
+--------+------------+-------+---------+
14 rows in set (0.00 sec)

子查询

就是在sql语句中,再嵌套select,被嵌套的select语句就是子查询

在where 中嵌套select

案例:显示出多大于平均工资的员工姓名

select ename from emp where sal>(select avg(sal) from emp);

输出结果

+-------+
| ename |
+-------+
| JONES |
| BLAKE |
| CLARK |
| SCOTT |
| KING  |
| FORD  |
+-------+
6 rows in set (0.01 sec)
在from 中嵌套select

显示出每个部门的平均工资,以及平均工资的工资等级

分析:应该分两部来做

1.显示每个部门的平均工资

select deptno, avg(sal) as 'avg'  from emp group by deptno; 
+--------+-------------+
| deptno | avg         |
+--------+-------------+
|     10 | 2916.666667 |
|     20 | 2175.000000 |
|     30 | 1566.666667 |
+--------+-------------+
3 rows in set (0.00 sec)

2.联合salgrade表

mysql> select * from salgrade;
+-------+-------+-------+
| GRADE | LOSAL | HISAL |
+-------+-------+-------+
|     1 |   700 |  1200 |
|     2 |  1201 |  1400 |
|     3 |  1401 |  2000 |
|     4 |  2001 |  3000 |
|     5 |  3001 |  9999 |
+-------+-------+-------+

最终的写法是

select 
a.*,s.grade 
from 
(select deptno, avg(sal) as 'avg'  from emp group by deptno)  a  
left join 
salgrade s 
on
a.avg between s.losal and s.hisal;
+--------+-------------+-------+
| deptno | avg         | grade |
+--------+-------------+-------+
|     10 | 2916.666667 |     4 |
|     20 | 2175.000000 |     4 |
|     30 | 1566.666667 |     3 |
+--------+-------------+-------+
3 rows in set (0.00 sec)

3.在select中嵌套select

union

可以将结果集合并

案例:找出工作岗位为salesman 和 manager的员工

有三种方法:

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

3.使用合并

select ename from emp where job='salesman'
union
select ename from emp where job='manager';

输出结果都是

+--------+
| ename  |
+--------+
| ALLEN  |
| WARD   |
| MARTIN |
| TURNER |
| JONES  |
| BLAKE  |
| CLARK  |
+--------+
7 rows in set (0.01 sec)
union还可以合并两张不相干的表

比如将部门名与员工名合并

mysql> select ename from emp
    -> union
    -> select dname from dept;
+------------+
| ename      |
+------------+
| SMITH      |
| ALLEN      |
| WARD       |
| JONES      |
| MARTIN     |
| BLAKE      |
| CLARK      |
| SCOTT      |
| KING       |
| TURNER     |
| ADAMS      |
| JAMES      |
| FORD       |
| MILLER     |
| ACCOUNTING |
| RESEARCH   |
| SALES      |
| OPERATIONS |
+------------+
18 rows in set (0.00 sec)

*limit

取结果集中的部分数据,相当于切片,limitmysql中特有的,其他数据库中没有

语法机制

limit startIndex,length

startIndex表示起始的位置

length表示取几个

案例取出工资前5名的员工

select ename,sal from emp order by sal desc limit 0 ,5;
+-------+---------+
| ename | sal     |
+-------+---------+
| KING  | 5000.00 |
| SCOTT | 3000.00 |
| FORD  | 3000.00 |
| JONES | 2975.00 |
| BLAKE | 2850.00 |
+-------+---------+

案例取出排名在5到10的员工

select ename,sal from emp order by sal desc limit 5 ,5;
+--------+---------+
| ename  | sal     |
+--------+---------+
| CLARK  | 2450.00 |
| ALLEN  | 1600.00 |
| TURNER | 1500.00 |
| MILLER | 1300.00 |
| MARTIN | 1250.00 |
+--------+---------+

应用:在实际的项目开发中,经常用到limit做分页数据显示

一个公式: 页码:pageno 一页显示的记录pagesize

limit应该这么写:

limit (pageno-1)*pagesize , pagesize

limit是sql语句最后执行的一个环节

你可能感兴趣的:(mysql)