1、统计函数
在之前学习过一个COUNT()函数,此函数的功能可以统计出表中的数据量,实际上这个就是一个统计函数,而常用的统计函数有如下几个:
范例:测试COUNT()、AVG()、SUM()
统计出公司的所有雇员,每个月支付的平均工资及总工资。
SELECT MAX(sal),MIN(sal) FROM emp;
注意点:关于COUNT()函数
COUNT()函数的主要功能是进行数据的统计,但是在进行数据统计的时候,如果一张表中没有统计记录,COUNT()也会返回数据,只是这个数据是“0”。
SELECT COUNT(ename) FROM BONUS;
如果使用的是其他函数,则有可能返回null,但是COUNT()永远都会返回一个具体的数字,这一点以后在开发之中都会使用到。
2、分组查询
在讲解分组操作之前首先必须先明确一点,什么情况下可能分组,例如:
这些信息如果都保存了数据库之中,肯定在数据的某一列上会存在重复的内容,例如:按照性别分组的时候,性别肯定有重复(男和女),按照年龄分组(有一个范围的重复),按照地区分组有一个地区的信息重复。
所以分组之中有一个不成文的规定:当数据重复的时候分组才有意义,因为一个人也可以一组(没什么意义)。
SELECT [DISTINCT] *|分组字段1 [别名] [,分组字段2 [别名] ,…] | 统计函数 FROM 表名称 [别名], [表名称 [别名] ,…] [WHERE 条件(s)] [GROUP BY 分组字段1 [,分组字段2 ,…]] [ORDER BY 排序字段 ASC | DESC [,排序字段 ASC | DESC]];
范例:按照部门编号分组,求出每个部门的人数,平均工资
SELECT deptno, COUNT(empno), AVG(sal) FROM emp GROUP BY deptno;
范例:按照职位分组,求出每个职位的最高和最低工资
SELECT job, MAX(sal), MIN(sal) FROM emp GROUP BY job;
但是现在一旦分组之后,实际上对于语法上就会出现了新的限制,对于分组有以下要求:
分组函数单独使用: |
SELECT COUNT(empno) FROM emp; |
错误的使用,出现了其他字段: |
SELECT empno,COUNT(empno) FROM emp; |
正确做法: |
SELECT job,COUNT(empno),AVG(sal) FROM emp GROUP BY job; |
错误的做法: |
SELECT deptno,job,COUNT(empno),AVG(sal) FROM emp GROUP BY job; |
范例:按照职位分组,统计平均工资最高的工资
1、先统计出各个职位的平均工资
SELECT job,AVG(sal) FROM emp GROUP BY job;
2、平均工资最高的工资
SELECT MAX(AVG(sal)) FROM emp GROUP BY job;
范例:查询出每个部门的名称、部门的人数、平均工资
1、确定所需要的数据表:
2、确定已知的关联字段:emp.deptno=dept.deptno;
范例:将dept表和emp表的数据关联
SELECT d.dname,e.empno,e.sal FROM dept d, emp e WHERE d.deptno=e.deptno;
DNAME EMPNO SAL -------------- ---------- ---------- ACCOUNTING 7782 2450 ACCOUNTING 7839 5000 ACCOUNTING 7934 1300 RESEARCH 7369 800 RESEARCH 7876 1100 RESEARCH 7902 3000 RESEARCH 7788 3000 RESEARCH 7566 2975 SALES 7499 1600 SALES 7698 2850 SALES 7654 1250 SALES 7900 950 SALES 7844 1500 SALES 7521 1250 已选择14行。
此时的查询结果中,可以发现在dname字段上显示出了重复的数据,按照之前对分组的理解,只要数据重复了,那么就有可能进行分组的查询操作,但是此时与之前的分组不太一样,之前的分组是针对于一张实体表进行的分组(emp、dept都属于实体表),但是对于以上的数据是通过查询结果显示的,所以是一张临时的虚拟表,但是不管是否是实体表还是虚拟表,只要是有重复,那么就直接进行分组。
SELECT d.dname,COUNT(e.empno),AVG(e.sal) FROM dept d, emp e WHERE d.deptno=e.deptno GROUP BY d.dname;
但是这个分组并不合适,因为部门一共有四个部门(因为现在已经引入了dept表,dept表存在了四个部门的信息),所以应该通过左右连接改变查询的结果。
SELECT d.dname,COUNT(e.empno),NVL(AVG(e.sal),0) FROM dept d, emp e WHERE d.deptno=e.deptno(+) GROUP BY d.dname;
之前的所有操作都是针对于单个字段分组的,而实际上分组操作之中也可以实现多字段分组。
范例:要求显示每个部门的编号、名称、位置、部门的人数、平均工资
1、确定所需要的数据表:
2、确定已知的关联字段:emp.deptno=dept.deptno;
范例:将emp表和dept表关联查询
SELECT d.deptno,d.dname,d.loc,e.empno,e.sal FROM dept d,emp e WHERE d.deptno=e.deptno(+);
DEPTNO DNAME LOC EMPNO SAL ---------- -------------- ------------- ---------- ---------- 10 ACCOUNTING NEW YORK 7782 2450 10 ACCOUNTING NEW YORK 7839 5000 10 ACCOUNTING NEW YORK 7934 1300 20 RESEARCH DALLAS 7369 800 20 RESEARCH DALLAS 7876 1100 20 RESEARCH DALLAS 7902 3000 20 RESEARCH DALLAS 7788 3000 20 RESEARCH DALLAS 7566 2975 30 SALES CHICAGO 7499 1600 30 SALES CHICAGO 7698 2850 30 SALES CHICAGO 7654 1250 30 SALES CHICAGO 7900 950 30 SALES CHICAGO 7844 1500 30 SALES CHICAGO 7521 1250 40 OPERATIONS BOSTON 已选择15行。
此时存在了重复数据,而且这个重复的数据平均在了三列上(deptno,dname,loc),所以在分组上的GROUP BY子句中就可以写上三个字段:
SELECT d.deptno,d.dname,d.loc,COUNT(e.empno),NVL(AVG(e.sal),0) FROM dept d,emp e WHERE d.deptno=e.deptno(+) GROUP BY d.deptno,d.dname,d.loc;
以上就是多字段分组,但是不管是单字段还是多字段,一定要有一个前提,存在了重复数据。
范例:要求统计出每个部门的详细信息,并且要求这些部门的平均工资高于2000;
在以上程序的基础上完成开发,在之前唯一所学习的限定查询的语法只有WHERE子句,所以下面先使用WHERE完成要求。
SELECT d.deptno,d.dname,d.loc,COUNT(e.empno) mycount,NVL(AVG(e.sal),0) myavg FROM dept d,emp e WHERE d.deptno=e.deptno(+) AND AVG(e.sal)>2000 GROUP BY d.deptno,d.dname,d.loc;
现在出现了如下的错误提示:
WHERE d.deptno=e.deptno(+) AND AVG(e.sal)>2000 * 第 3 行出现错误: ORA-00934: 此处不允许使用分组函数
本错误提示的核心意思就是在WHERE子句之中不能使用统计函数,之所以在WHERE子句之中不能使用,实际上跟WHERE子句的主要功能有关,WHERE的主要功能是从全部的数据之中取出部分数据。
此时如果要对分组后的数据再次进行过滤,则使用HAVING子句完成,那么此时的SQL语法格式如下:
SELECT [DISTINCT] *|分组字段1 [别名] [,分组字段2 [别名] ,…] | 统计函数 FROM 表名称 [别名], [表名称 [别名] ,…] [WHERE 条件(s)] [GROUP BY 分组字段1 [,分组字段2 ,…]] [HAVING 分组后的过滤条件(可以使用统计函数)] [ORDER BY 排序字段 ASC | DESC [,排序字段 ASC | DESC]];
下面使用HAVING进行过滤。
SELECT d.deptno,d.dname,d.loc,COUNT(e.empno) mycount,NVL(AVG(e.sal),0) myavg FROM dept d,emp e WHERE d.deptno=e.deptno(+) GROUP BY d.deptno,d.dname,d.loc HAVING AVG(sal)>2000;
注意点:WHERE和HAVING的区别
思考题:显示非销售人员工作名称以及从事同一工作雇员的月工资的总和,并且要满足从事同一工作的雇员的月工资合计大于$5000,输出结果按月工资的合计升序排列:
第一步:查询出所有的非销售人员的信息
SELECT * FROM emp WHERE job<>'SALESMAN';
第二步:按照职位进行分组,并且使用SUM函数统计
SELECT job,SUM(sal) FROM emp WHERE job<>'SALESMAN' GROUP BY job;
第三步:月工资的合计是通过统计函数查询的,所以现在这个对分组后的过滤要使用HAVING子句完成
SELECT job,SUM(sal) FROM emp WHERE job<>'SALESMAN' GROUP BY job HAVING SUM(sal)>5000;
第四步:按照升序排列
SELECT job,SUM(sal) sum FROM emp WHERE job<>'SALESMAN' GROUP BY job HAVING SUM(sal)>5000 ORDER BY sum ASC;
以上的题目就融合分组操作的大部分语法的使用,而且以后遇到问题,要慢慢分析。