【MySQL】复合查询与内外连接
目录
一、复合查询
1、基本查询回顾:
2、多表查询:
3、自连接:
4、子查询:
单列子查询
多行子查询:
多列子查询:
在from语句中使用子查询:
5、合并查询:
union:
union all:
二、内外连接:
1、内连接:
2、外连接:
左外连接:
右外连接:
这里依然是使用经典数据表:员工表(emp)、部门表(dept)和工资等级表(salgrade)
如下是员工表,分别是empno员工号/ename员工姓名/job工作/mgr上级编号/hiredate受雇日期/sal薪金/comm佣金/deptno所属部门编号
如下是部门表,分别是deptno部门编号,dname部门名称,loc部门所在地
如下是薪资等级:
一、复合查询
1、基本查询回顾:
查询工资高于500或岗位为MANAGER的员工,同时还要满足他们的名字的首字母大写的J
select * from emp where (sal>500 or job='MANAGER') and ename like 'J%';
除了用模糊匹配,还有字符串切割也可以
select * from emp where (sal>500 or job='MANAGER') and substring(ename,1,1)='J';
查询员工信息,按部门号升序而员工工资降序显示
select ename,sal,deptno from emp order by deptno asc ,sal desc;
查询员工信息,按年薪降序显示
select * from emp order by 12*sal+ifnull(comm,0) desc;
员工的年薪为月工资*12+年终奖,但是这里不能够直接12*sal+comm,因为comm可能为NULL,此时如果用NULL运算就会使结果为空
所以需要使用函数ifnull,如果comm为NULL就将其设置为0
查询工资最高的员工的姓名和岗位
首先查看工资最高是谁,这里使用聚合函数:
select max(sal) from emp;
当查询到最高工资的人后,可以将这个结果放到where语句中作为判断条件,也就是说where语句中可以进行查询:
select ename,job from emp where sal=(select max(sal) from emp);
查询工资高于平均工资的员工信息
首先依然是查询员工的平均工资:
select avg(sal) from emp;
接着将这个语句嵌套在where中作为判断:
select * from emp where sal>(select avg(sal) from emp);
查询每个部门的平均工资和最高工资
既然是每个部门,就需要进行分组
select deptno,avg(sal) 平均工资,max(sal) 最高工资 from emp group by deptno;
查询平均工资低于2000的部门号和它的平均工资
select deptno,avg(sal) 平均工资 from emp group by deptno having avg(sal)<2000;
这里因为是进行分组查询,不能使用where,需要使用having来进行条件判断
查询每种岗位的雇员总数和平均工资
人数用count()函数进行统计
select job,count(*) 人数,avg(sal) 平均工资 from emp group by job;
2、多表查询:
在上述基本查询中,都是在单表中进行查询的,但是在实际开发中,更多的是多个表综合起来进行查询的,这就叫做多表查询
笛卡尔积:
在进行多表查询的时候,将多个表名放在from后面并用逗号隔开,这是,MySQL就会对这些表取笛卡尔积,组成一张新表
进行笛卡尔积转化后:
笛卡尔积的本质是拿着第一张表的信息 依次 和第二张表的所有信息进行组合,这样形成地一张表
多表查询的本质:对给的多张表取笛卡尔积,然后对笛卡尔积后的表进行查询
在进行笛卡尔积的多张表中可能会存在相同的列名,这时在选中列名时需要通过(表名.列明)的方式进行指明
显示雇员名,雇员工资以及所在部门的名字
这里雇员名和雇员工资是在同一张表中的,但是所在部门名字是在另一张表中的,所以需要对这两张表进行笛卡尔积,然后在进行查询即可
通过上述图片可以看到,比如Smith他的部门号有两个,这里是取相等的,其他的就是没有意义的数据,所以需要进行初步筛选:
select * from emp,dept where emp.deptno=dept.deptno;
最后在将*修改为所需即可
select ename,sal,dname from emp,dept where emp.deptno=dept.deptno;
显示部门号为10的部门名、员工名和员工工资
这里部门名和其他是在不同的表中的
select dname,ename,sal from emp,dept where emp.deptno=dept.deptno and emp.deptno=10;
显示各个员工的姓名、工资和工资级别
这里工资级别和其他是不同的表中:
需要where增加的条件是工资处于最低和最高之间工资筛选出来,才有意义
3、自连接:
自连接是在同一张表进行连接查询,也就是说对同一张表进行取笛卡尔积,
显示员工FORD的上级领导的编号和姓名
子查询解决:
首先找到FORD的上级领导的编号,
select mgr from emp where ename='FORD';
接着通过嵌套找到对应编号的姓名
select empno,ename from emp where empno=(select mgr from emp where ename='FORD');
自连接解决:
员工表中的mgr字段能够将表中员工的信息和员工领导的信息关联起来
mysql> select leader.empno,leader.ename from emp leader,emp worker-> where worker.ename='FORD' and leader.empno=worker.mgr;
这里是对同一张表进行取笛卡尔积的,所以需要对其取别名来区别开来
4、子查询:
子查询是指嵌入在其他sql语句中的select语句,也叫嵌套查询
子查询可分为单行子查询,多行子查询,多列子查询,以及在from子句中使用的子查询
单列子查询
显示SMITH同一部门的员工
首先显示SMITH的部门
select deptno from emp where ename='SMITH';
接着将这个语句嵌套在where判断中,作为嵌套的子语句,再来查询对应的员工
select * from emp where deptno=(select deptno from emp where ename='SMITH') and ename<>'SMITH';
多行子查询:
返回多行单列数据的子查询
in关键字:查询和 10 号部门的工作岗位相同的雇员的名字,岗位,工资,部门号,但是不包含 10 自己的
首先查询10号部门的工作岗位,这里要进行去重,因为可能不同的人在同一个部门有着相同的工作岗位
这里查出来和之前不同的是这里查询的是单列多行的,只要岗位和这里面一个相同就可以,用in关键字
select ename,job,sal,deptno from emp where job in(select distinct job from emp where deptno=10) and deptno<>10;
all关键字:显示工资比30号部门的所有员工的工资高的员工的姓名、工资和部门号
先查询30号部门的所有员工的工资,因为工资可能相等,所以这里最好去重
接着在进行嵌套,为了保证所查出来的工资比30号部门的所有员工的工资高,这里使用all关键字
any关键字:显示工资比30号部门的任意员工的工资高的员工的姓名、工资和部门号,包含30号部门的员工
首先查询30号部门的员工的工资:
select distinct sal from emp where deptno=30;
接着通过关键字any进行员工的查看:
select ename,sal,deptno from emp where sal>any(select distinct sal from emp where deptno=30);
多列子查询:
这是返回多列多行的查询
显示和SMITH的部门和岗位完全相同的员工,不包含SMITH本人
首先显示SMITH的部门和岗位:
select deptno,job from emp where ename='SMITH';
接着通过复合查询,将上述的查询放在where后面作为子查询
这里是采用的多列查询,所以在where后面匹配的时候通过括号进行多列匹配,并且在后面记得保证名字不能为SMITH
也就是说,多列子查询在where匹配的时候要用括号将多列数据进行比较,并且如果数据是多行的,也可以使用in,all,any关键字
在from语句中使用子查询:
我们知道from后面跟着的是表,在MySQL下一切皆表,所以可以将一个查询结果当做临时表,放在from语句的后面
显示每个高于自己部门平均工资的员工的姓名、部门、工资和部门的平均工资
首先查询每一个部门其自己的平均工资:
select deptno,avg(sal) from emp group by deptno;
接着将如上表和员工表取笛卡尔积,然后再通过部门号相等删除部分无效数据,最后where筛选出高于自己部门平均工资的员工的数据
mysql> select ename,emp.deptno,sal,平均工资-> from emp,(select deptno,avg(sal) 平均工资 from emp group by deptno) newtable-> where emp.deptno=newtable.deptno and sal>平均工资;
注意:在from子句的查询中,必须给子查询所生成的临时表取一个别名,否则查询结果会出错找不到对应的字段,并且如果两张表中有相同的字段,要指定其是在哪张表的,否则也会报错
显示每个部门工资最高的员工的姓名、工资、部门和部门的最高工资
首先查询每个部门的最高工资的员工:
select deptno,max(sal) from emp group by deptno;
接着将上述表和员工表进行笛卡尔积,在进行筛选即可
mysql> select ename,sal,emp.deptno,最高工资-> from emp,(select deptno,max(sal) 最高工资 from emp group by deptno) newtable-> where emp.deptno=newtable.deptno and sal=最高工资;
5、合并查询:
为了合并多个select查询结果,可以通过操作符union和union all进行合并查询
union:
用于取得两个查询结果的并集,union会自动去掉结果集中的重复行
显示工资大于2500或职位是MANAGER的员工
首先查询工资大于2500员工:
select * from emp where sal>2500;
接着查询职位是MANAGER的员工:
select * from emp where job='MANAGER';
为了完成上述,可以使用or关键字:
select * from emp where sal>2500 or job='MANAGER';
或者使用union关键字:
select * from emp where sal>2500 union select * from emp where job='MANAGER';
union all:
该操作符用于取得两个结果集的并集,与union不同的是,这个不会对结果进行去重:
select * from emp where sal>2500 union all select * from emp where job='MANAGER';
注意:待合并的两个查询结果的列的数量必须一致,否则无法合并
二、内外连接:
表的连接分为内连接和外连接
1、内连接:
内连接实际上就是利用where子句对两种表形成的笛卡儿积进行筛选,我们前面学习的查询都是内连接,也是在开发过程中使用的最多的连接查询
语法:
select 字段 from 表1 inner join 表2 on 连接条件 and 其他条件;
显示 SMITH 的名字和部门名称
其实完成上述查询可以不使用内连接,用以前学习的已经够了,只是内连接能够让我们的查询逻辑更清楚
在上述的查询中,我们可以使用以前学习的笛卡尔积
select emp.ename,dept.dname from emp,dept where emp.deptno=dept.deptno and emp.ename='SMITH';
除了笛卡尔积这种写的方式,我们还可以用内连接的写法:
select ename,dname from emp inner join dept on emp.deptno=dept.deptno and ename='SMITH';
2、外连接:
左外连接:
如果想让左侧的表完全显示,右侧的表如果和左侧的表没有匹配的就去掉,就使用左外连接,其语法和内连接一模一样只是将inner这个关键字修改为left
select 字段 from 表1 left join 表2 on 连接条件 and 其他条件;
示例:
首先创建一个测试表:
查询所有学生的成绩,如果这个学生没有成绩,也要将学生的个人信息显示出来
select * from stu left join exam on stu.id=exam.id;
尽管在exam这个右侧表中,没有王五赵六的成绩信息,但是他们仍然被显示出来了,左连接更偏向于左边的表
右外连接:
右外连接和左外连接就是相反的了,比如上述要求我们改为:
查询所有成绩,如果这个成绩没有对应的学生,也要将这个成绩显示出来
select * from stu right join exam on stu.id=exam.id;
但事实上,左外连接和右外连接是可以相互转换的