通过下面的练习能够评估你的SQL查询的基本功。
雇员信息表数据库文件
部门编号(deptno),部门名称(dname),部门所在地点(loc)。
等级(grade),此等级最低工资(losal),此等级最高工资(hisal)。
1.1 查询工资高于500或岗位为MANAGER的员工,同时要求员工姓名的首字母为大写的J
ename
,job
,sal
都在emp
表中,所以from
中的表格应该是emp
。500
或岗位为MANAGER
的员工,我们可以使用(sal > 500 or job = MANAGER)
。J
我们只需要使用left(ename, 1) = 'J'
and
完成select ename, job,sal from emp where (sal>500 or job='MANAGER') and (left(ename, 1) = 'J');
1.2 查询员工信息,按部门号升序而员工工资降序显示
order by
子句是支持按照多个属性进行排序的,且书写的顺序就是排序的优先级。order by deptno asc, sal desc
。1.3 使用年薪进行降序排序
sal
列进行数据运算即可求出年薪。NULL
,由于NULL
是无法参与运算的,所以我们还要对奖金进行特殊处理,如果奖金是NULL
我们就使用0
进行运算,如果是非NULL
我们就直接运算,所以这里我们可以使用ifnull(comm,0)
select ename, sal*12+ifnull(comm, 0) 年薪 from emp;
1.4 显示工资最高的员工的名字和工作岗位
sal
是用聚合函数max
来求出。我们发现这样的SQL是能够满足我们的要求的,但是这样写法有一些散乱,我们其实可以使用子查询将它们连接在一起,(子查询的详细信息我们后面进行讲解)
select ename, sal from emp where sal = (select max(sal) from emp);
1.5 显示工资高于平均工资的员工信息
avg(sal)
where
条件进行按照工资大于平均工资进行晒选。这里写成两条SQL的写法过于简单,我就不再书写了,这里我们仿照这上面的子查询写成一条。
select ename, sal from emp where sal > (select avg(sal) from emp);
1.6 显示每个部门的平均工资和最高工资
avg(sal)
max(sal)
1.7 显示平均工资低于2000的部门号和它的平均工资
group by
子句中指明按照部门号进行分组,然后使用avg
函数求得每个部门的平均工资。having
子句中指明筛选条件为平均工资小于2000
。select deptno, avg(sal) 平均工资 from emp group by deptno having 平均工资< 2000;
1.8 显示每种岗位的雇员总数,平均工资
group by
子句中指明按照job
进行分组。count
函数统计雇员总数,使用avg()
函数求得平均工资。select job, count(*), avg(sal) from emp group by job;
上面的基础查询都是在一张表的基础上进行的查询,实际开发中往往数据来自不同的表,所以需要多表查询。
这里我们继续使用这三张表:EMP,DEPT,SALGRADE
来演示如何进行多表查询。
多表查询的语法:
from
子句之后,用逗号隔开即可,这时MySQL将会对给定的这多张表取笛卡尔积,作为多表查询的初始数据源。....from table_1, table_2, table_4 ....
例如我们对下面这两张小表做笛卡尔积:
select * from dept, salgrade;
可以看出:对部门表和薪资等级表取笛卡尔积时,会先从薪资等级表中选出一条记录与部门表中的所有记录进行组合,然后再从薪资等级表中选出一条记录与部门表中的所有记录进行组合,以此类推,最终得到的就是这两张表的笛卡尔积。
需要注意的是,对多张表取笛卡尔积后得到的数据并不都是有意义的,比如对员工表和部门表取笛卡尔积时,员工表中的每一个员工信息都会和部门表中的每一个部门信息进行组合,而实际一个员工只有和自己所在的部门信息进行组合才是有意义的。
所以我们可以再where
子句中指明:emp.deptno = dept.deptno
,即从笛卡尔积中筛选出员工的部门号和部门的编号相等记录。
说明: 进行笛卡尔积的多张表中可能会存在相同的列名,这时在选中列名时需要通过表名.列名
的方式进行指明。
可以看到,这种一个员工只和自己所在的部门信息进行组合的表格才是正真有意义的表格。
下面我们来进行一些练习:
1.1 显示部门号为10的部门名,员工名和工资
ename
)和工资(sal
)这两个字段在emp
表中,而部门名称在dept
表中,所以我们要使用多表查询from emp, dept
。where
条件中使用emp.deptno = dept.deptno
10
的部门,所以我们可以使用where
条件进行筛选dept.deptno = 10
,当然由于emp
中也有deptno
字段,所以我们也可以使用emp.deptno = 10
。select ename, dept.deptno, dname from emp, dept where (emp.deptno=dept.deptno) and (dept.deptno = 10);
1.2 显示各个员工的姓名,工资,及工资级别
ename
)和工资(sal
)这两个字段在emp
表中,而工资级别在salgrade
表中,所以我们要使用多表查询from emp, salgrade
。where
子句中添加限制条件: sal between losal and hisal
select ename, sal, grade , losal, hisal from emp, salgrade where sal between losal and hisal;
刚才我们使用多表查询是:两张不同的表进行组合,那么对于两张相同的表能不能进行组合呢?
答案是可以的:对同一张表连接查询我们称之为自连接
语法:
我们直接像多表查询那样在from
后面写上两次表名是不行的!因为两张表同名的话,我们后面对列进行各种操作时有歧义。
select * from dept,dept;
所以我们需要对表进行重命名,以保证我们后面对列进行各种操作时没有歧义!
select * from dept as t1, dept as t2;
下面我们来看一个例子帮我们更好的理解自连接:
3. 1 显示员工FORD的上级领导的编号和姓名(mgr是员工领导的编号,empno是雇员编号)
此外员工表中的mgr
字段能够将表中员工的信息和员工领导的信息关联起来
方法一:使用多条SQL
FORD
其领导的编号select mgr from emp where ename = 'FORD';
select * from emp where empno=7566;
方法二: 使用子查询
先对员工表进行查询得到FORD
的领导的编号,然后再根据领导的编号对员工表进行查询得到FORD
领导的信息。
select * from emp where empno=(select mgr from emp where ename='FORD');
方法三:使用自连接
我们发现两次查找都是在emp
表中进行查找,所以我们可以对emp
表进行自连接,然后对自连接的结果按照 :
e1
表的雇员名称必须是FORD
而且员工的领导编号等于领导的员工编号即:(e1.ename='FORD') and (e1.mgr=e2.empno)
的方式进行过滤。
select e2.ename,e2.empno from emp e1, emp e2 where (e1.ename='FORD') and (e1.mgr=e2.empno);
sql
语句中的select
语句,也叫嵌套查询from
子句中使用的子查询所谓的单行子查询就是,返回一行记录的子查询。
1.1 显示SMITH同一部门的员工
select ename, deptno from emp where deptno=(select deptno from emp where ename='SMITH');
我们知道select
的查询结果可以是多条记录,所以返回多条记录的子查询我们称之为多行子查询。
下面我们借助一些问题,来学习一下多行子查询中的一些关键字。
- in关键字: 此关键字用于检查某个值是否存在于子查询返回的结果集中
2.1 查询和10号部门的工作岗位相同的雇员的名字,岗位,工资,部门号,但是不包含10自己的。
首先理解题意:10号部门中有许多的工作岗位,我们需要在整张表中去筛选员工的工作岗位和10号部门中的工作岗位是相同的,最后我们还要去掉处于10号部门中的人。
select
将10号部门中的工作岗位全部筛选出来。where
条件中,将第一步返回的结果作为一个筛选条件,由于第一步返回的是一张多行的表格,而我们只需要满足其中的一个条件就满足了要求,所以这里我们可以使用in
关键字(****)select ename, job,sal,deptno from emp where (job in (select job from emp where deptno=10)) and (deptno != 10);
- all关键字: 此关键字通常与比较操作符(如=、>、<、>=、<=、<>或<>)一起使用,以将某个值与子查询返回的所有结果进行比较
2.2 显示工资比30号部门的所有员工的工资高的员工的姓名、工资和部门号
方法一:使用单行子查询
比30号部门的所有员工的工资高,这句话意味着只要我们比30号部门的最高工资高我们就能够满足题目要求。
所以我们可以直接使用聚合函数max
完成我们的要求,当然使用max
其实意味着我们使用一个单行子查询就能完成我们要求。
select ename, sal, deptno from emp where sal > (select max(sal) from emp where deptno=30);
方法二:多行子查询
我们就按题目的要求,要比30号部门的所有员工的工资高。
where
筛选条件,由于第一步返回的是一张多行的表格,而我们需要满足所有条件才能满足了要求,所以这里我们可以使用all
关键字(表示满足其中的所有的条件,才成立)select ename, sal, deptno from emp where sal > all (select sal from emp where deptno=30);
- any关键字: 此关键字用于与比较运算符一起使用,以比较某个值与子查询返回的任何一个结果。
2.3 显示工资比部门30的任意员工的工资高的员工的姓名、工资和部门号(包含自己部门的员工)
和上面的题目一样,第一种方法我们直接找30号部门的最低工资,当然这里为了介绍any
关键字,我们采用多行子查询的方式进行解决这个问题。
where
筛选条件,由于第一步返回的是一张多行的表格,而我们只需要满足任意一个条件就能满足要求,所以这里我们可以使用any
关键字(表示满足其中的任意一个条件,就能成立)select ename, sal,deptno from emp where sal > any (select distinct sal from emp where deptno=30);
最后是关于
in
和any
的辨析
in
关键字:in
关键字用于检查某个值是否存在于子查询返回的结果集中,如果子查询返回多个结果,in
关键字会检查列的值是否匹配子查询中的任何一个结果。
any
关键字:any
关键字用于与比较运算符一起使用,以比较某个值与子查询返回的任何一个结果。
总结:
in
关键字是检查值是否存在于子查询的结果集中。any
关键字是用于与比较运算符一起使用,以比较值与子查询的任何一个结果。例如上面笔者给的in
与any
的练习中,你会发现in
与any
是不能相互替换的。
单行子查询是指子查询只返回单行单列数据,多行子查询是指返回单列多行数据,然而它们都是针对单列而言,而多列子查询则是指查询返回多个列数据的子查询语句。
3.1 查询和SMITH的部门和岗位完全相同的所有雇员,不含SMITH本人
SMITH
的部门和岗位。where
筛选条件,由于第一步返回的是一张单行多列的表格,而我们又需要同时比较两个条件,(其他人的部门和岗位要与SMITH完全相同),所以这里我们可以对要比较的数据加上一个()
中间使用,
分割要比较的字段,表示一次比较多个字段。select ename, job,deptno from emp where (job,deptno)= (select job,deptno from emp where ename='SMITH') and ename != 'SMITH';
说明一下:
where
子句中,充当判断条件用的。form
子句中使用,用于充当一个子表。我们来看下面的案例:
4.1 显示每个高于自己部门平均工资的员工的姓名、部门、工资、平均工资
select deptno, avg(sal) from emp group by deptno
emp.deptno = tmp.deptno
必须相同。where
条件中进行筛选员工的工资要高于平均工资sal > avg_sal
select ename, emp.deptno, sal, avg_sal from emp, (select deptno, avg(sal) avg_sal from emp group by deptno) as tmp where (emp.deptno=tmp.deptneptno) and (sal > avg_sal);
说明: 在from子句中使用子查询时,必须给子查询得到的临时表取一个别名,否则查询将会出错。
4.2 显示每个部门工资最高的员工的姓名、工资、部门和部门的最高工资
max
求出。select deptno,max(sal) max_sal from emp group by deptno;
emp.deptno = tmp.deptno
必须相同。where
条件中进行筛选员工的工资要等于最高工资sal = max_sal
select ename, sal, emp.deptno, max_sal from emp, (select deptno,max(sal) max_sal from emp group by deptno) as tmp where (emp.deptno=tmp.deptnoptno) and (sal = max_sal);
4.3 显示每个部门的信息(部门名,编号,地址)和人员数量
select deptno, count(*) total from emp group by deptno;
dept.deptno = tmp.deptno
必须相同。select dname,dept.deptno,loc,total from dept, (select deptno, count(*) total from emp group by deptno) as tmp where dept.deptno=tmp.deptno;
学习完这些复合查询以后我们可以体会到:
解决多表问题的本质: 就是想办法将多表转化成为单表,所以mysql
中,所有select
的问题,全部都可以转成单表问题!
在实际应用中,为了合并多个select
的执行结果,可以使用集合操作符 union,union all
union all
用于取得两个查询结果的并集,但union all
不会去掉结果集中的重复行。union
也用于取得两个查询结果的并集,union
会自动去掉结果集中的重复行。4.1 显示工资大于2500或职位是MANAGER的员工
对于这个需求,我们使用where
与or
运算符很容易解决,当然这里为了介绍合并查询我们这里使用两条SQL语句:
select * from emp where sal > 2500;
select * from emp where job='MANAGER';
这里为了满足题目的要求我们我们使用union all
将两条SQL连接起来:
select * from emp where sal > 2500 union all select * from emp where job='MANAGER';
结果确实是我们想要的,但是我们会发现结果中有重复的,因为有员工工资即大于2500
,又是MANAGER
因此这里我们可以使用union
,union
会自动去掉结果集中的重复行。
说明: