
4.4 操作查询结果
使用SELECT语句查询数据时,可以对查询的结果进行排序、分组和统计。一旦为查询结果集进行了排序、分组或统计,就可以方便用户查询数据。
4.4.1 对查询结果排序
在前面介绍的数据检索技术中,只是把数据库中的数据从表中直接取出来。这时,结果集中数据的排列顺序是由数据的存储顺序决定的。但是,这种存储顺序经常不符合开发人员的查询要求。当查询一个比较大的表时,数据的显示会比较混乱,因此需要对检索到的结果集进行排序。
在SELECT语句中,使用ORDER BY子句实现对查询的结果集进行排序。排序有两种方式:ASC表示升序,这也是默认的排序方式;DESC表示降序。
【范例17】
查询emp表中的前三条记录,并且将对sal表进行升序排列。语句和输出结果如下。
SELECT empno, ename, sal FROM emp WHERE ROWNUM<=3 ORDER BY sal; EMPNO ENAME SAL ------------------------- 7369 SMITH 800 7521 WARD 1250 7499 ALLEN 1600
将上述结果与范例16中的输出结果进行比较可以发现,ORDER BY关键字进行升序排序已经成功。
【范例18】
查询emp表中的前三条记录,并且将对sal表进行降序排列。语句和输出结果如下。
SELECT empno, ename, sal FROM emp WHERE ROWNUM<=3 ORDER BY sal DESC; EMPNO ENAME SAL ---------- ----------- ------- 7499 ALLEN 1600 7521 WARD 1250 7369 SMITH 800
【范例19】
开发人员可以在ORDER BY子句中使用别名或表达式。例如,在SELECT子句中计算出员工的年薪并指定别名FULLYEARSAL,然后在ORDER BY子句中根据指定的别名进行降序排列。语句和输出结果如下。
SELECT empno, ename, sal, (sal*12) FULLYEARSAL FROM emp WHERE ROWNUM<=3 ORDERBY FULLYEARSAL DESC; EMPNO ENAME SAL FULLYEARSAL ------- ----------- -------- ----------- 7499 ALLEN 1600 19200 7521 WARD 1250 15000 7369 SMITH 800 9600
【范例20】
如果需要对多个列进行排序,只需要在ORDER BY子句后指定多个列名。这样当输出排序结果时,首先根据第一列排序,当第一列的值相同时,再对第二列进行比较排序,其他列以此类推。
下面使用SELECT语句首先对sal列排序,然后再对ename列排序。代码如下。
SELECT empno, ename, sal FROM emp WHERE ROWNUM<=3 ORDER BY sal DESC, ename;
4.4.2 对查询结果分组
GROUP BY子句用于在查询结果集中对记录进行分组,以汇总数据或者为整个分组显示单行的汇总信息。
开发人员在使用GROUP BY子句时,必须满足以下几个条件。
(1)在SELECT子句的后面只可以有两类表达式:聚合函数和进行分组的列名。
(2)在SELECT子句中的列名必须是进行分组的列,除此之外添加其他的列表都是错误的。但是GROUP BY子句后面的列名可以不出现在SELECT子句中。
(3)如果使用了WHERE子句,那么所有参数分组计算的数据必须首先满足WHERE子句指定的条件。
(4)在默认情况下,将按照GROUP BY子句指定的分组列升序排列,如果需要重新排序,可以使用ORDER BY子句指定新的排列顺序。
【范例21】
在使用GROUP BY子句之前首先查询emp表中的记录,并且针对job列和sal列排序,语句和输出结果如图4-4所示。从图4-4中可以看出,对于每一个job列,都可以有多个对应的sal值。

图4-4 查询emp表中的记录
开发人员可以使用GROUP BY子句实现对查询结果中每一组数据进行分类统计。使用GROUP BY子句时,在结果中每组数据都有一个与之对应的统计值。GROUP BY子句实现分组功能时,通常会用到聚合函数,关于聚合函数会在第8章中介绍。
【范例22】
使用GROUP BY子句对job列进行分组,并且分别使用COUNT()函数、SUM()函数、AVG()函数、MAX()函数和MIN()函数计算每个职位(job列)的数据行数、所有工资总和、平均工资、最高工资和最低工资。语句和输出结果如下。
SELECT job, COUNT(job), SUM(sal), AVG(sal), MAX(sal), MIN(sal) FROM emp GROUP BYjob; JOB COUNT(JOB) SUM(SAL) AVG(SAL) MAX(SAL) MIN(SAL) --------- ------------ ---------- ---------- --------- -------- CLERK 4 4150 1037.5 1300 800 SALESMAN 4 5600 1400 1600 1250 PRESIDENT 1 5000 5000 5000 5000 MANAGER 3 8275 2758.33333 2975 2450 ANALYST 1 3000 3000 3000 3000
GROUP BY子句与ORDER BY子句很相似,GROUP BY子句也可以对多个列进行分组。在这种情况下,GROUP BY子句将在主分组范围内进行二次分组。
【范例23】
下面的语句对各部门中的各个工作类型进行分组。
SELECT deptno, job, COUNT(*), SUM(sal), AVG(sal) FROM emp GROUP BY deptno, job;
执行上述语句,输出结果如下。
DEPTNO JOB COUNT(*) SUM(SAL) AVG(SAL) ------- ----------- ---------- ------------ -------------------- 20 CLERK 2 1900 950 30 SALESMAN 4 5600 1400 20 MANAGER 1 2975 2975 30 CLERK 1 950 950 10 PRESIDENT 1 5000 5000 30 MANAGER 1 2850 2850 10 CLERK 1 1300 1300 10 MANAGER 1 2450 2450 20 ANALYST 1 3000 3000
GROUP BY子句中可以使用ROLLUP和CUBE运算符,这两个运算符在功能上相似。在GROUP BY子句中使用它们后,都将会在查询结果中附加一行汇总信息。
【范例24】
下面的语句在GROUP BY中使用ROLLUP运算符汇总job列。
SELECT job, COUNT(*), SUM(sal), AVG(sal) FROM emp GROUP BY ROLLUP(job);
执行上述语句,输出结果如下。
JOB COUNT(*) SUM(SAL) AVG(SAL) ---------- ------------- ------------- ------------- ANALYST 1 3000 3000 CLERK 4 4150 1037.5 MANAGER 3 8275 2758.33333 PRESIDENT 1 5000 5000 SALESMAN 4 5600 1400 13 26025 2001.92308
试一试
当使用CUBE运算符时也会在查询结果中附加一行汇总信息,但是它不是在查询结果的底部(最后一行),而是顶部(第一行),感兴趣的读者可以亲自动手试一试。
4.4.3 对查询结果筛选
HAVING子句通常与GROUP BY子句一起使用,在完成对分组结果统计之后,可以使用HAVING子句对分组的结果做进一步筛选。如果不使用GROUP BY子句, HAVING子句的功能与WHERE子句一样。HAVING子句和WHERE子句的相似之处就是都定义搜索条件,但是和WHERE子句不同,不同点表现在以下三个方面。
(1)HAVING针对结果组;WHERE针对的是列的数据。
(2)HAVING可以与聚合函数一起使用;但是WHERE不能。
(3)HAVING语句只过滤分组后的数据;WHERE在分组前对数据进行过滤。
【范例25】
下面的语句查询工作类型的员工人数不等于1的记录。
SELECT job, COUNT(*), SUM(sal), AVG(sal) FROM emp GROUP BY job HAVING COUNT(*)! =1;
执行上述语句,输出结果如下。
JOB COUNT(*) SUM(SAL) AVG(SAL) ----------------- -------------- --------------- --------------- CLERK 4 4150 1037.5 SALESMAN 4 5600 1400 MANAGER 3 8275 2758.33333
从上述查询结果可以看出,SELECT语句使用GROUP BY子句对emp表进行分组统计,然后再由HAVING子句根据统计值做进一步筛选。
提示
通常情况下,HAVING子句与GROUP子句一起使用,这样可以在汇总相关数据后再进一步筛选汇总的数据。在使用WHERE子句或HAVING子句都能查询出相同的结果时, WHERE子句放在GROUP BY子句之前,而HAVING子句放在GROUP BY子句之后。