多行函数也叫组函数,本章学习目标:
分组函数作用于一组数据,并对一组数据返回一个值。如:AVG、COUNT、MAX、MIN、SUM操作的是一组数据,返回一个结果。
求员工的工资总额:
SQL> ?select sum(sal) from emp; ? sum() 对指定列的各行求和。
员工人数:
SQL> ?select count(*) from emp; ? count() 统计指定列的非空行数。
平均工资:
SQL> select sum(sal)/count(*) 方式一, avg(sal) 方式二 ?from emp; ?
方式一和方式二结果一样,当有空值得时候结果有可能不一样。如:奖金。
求员工的平均奖金:
SQL> select sum(comm)/count(*) 方式一, ?sum(comm)/count(comm) 方式二, ?avg(comm) 方式三 ? ??from emp; ?
结果:方式一结果不同,方式二 和 方式三结果一样。
☆NULL空值:组函数都有自动滤空功能(忽略空值),所以:
SQL> select count(*), count(comm) from emp; ? 执行结果不相同。
如何屏蔽 组函数 的滤空功能:
SQL> select count(*), count(nvl(comm,0)) from emp; ?
但是实际应用中,结果为14和结果为4都有可能对,看问题本身是否要求统计空值。
count函数:求个数,如果要求不重复的个数,使用distinct。
求emp表中的工种:
SQL> select count(distinct job) from emp; ? ?
group by ?????
按照group by 后给定的表达式,将from后面的table进行分组。针对每一组,使用组函数。
查询“部门”的平均工资:
分析:结合select * from emp order?by deptno ??结果分析分组
SQL> select ?deptno, ?avg(sal) ?from emp ?group by deptno; ?
上SQL语句可以抽象成:select a, 组函数(x) from 表 group by a; ?这样的格式。
如果select a, b 组函数(x) …… group by 应该怎么写? ??
注意: 在SELECT 列表中所有没有包含在组函数中的列,都必须在group by的后面出现。所以上问应该写成group by a, b;没有b语法就会出错,不会执行SQL语句。但,反之可以。Group by a,b,c;??c可以不出现在select语句中。 ???
group by后面有多列的情况:
SQL> select deptno, job, avg(sal) ?from emp ?group by deptno, job ?order by 1; ???
分析该SQL的作用:
因为deptno, job 两列没有在组函数里面,所以必须同时在group by后面。
该SQL的语义:按部门,不同的职位统计平均工资。先按第一列分组,如果第一列相同,再按第二列分组。
??所以查询结果中,同一部门中没有重复的职位。
常见的非法使用组函数的情况,主要出现在缺少group?by 子句。如hr用户下执行查询语句:
SELECT department_id, COUNT(last_name)
FROM ?employees;
会显示如下错误:
SELECT department_id, COUNT(last_name)
???????*
ERROR at line 1:
ORA-00937: not a single-group group function
意为:GROUP BY 子句中缺少列
? ?
使用 HAVING 过滤分组:
1. 行已经被分组。
2. 使用了组函数。
3. 满足HAVING 子句中条件的分组将被显示。
其语法:
SELECT column, group_function
FROM table
[WHERE condition]
[GROUP BY group_by_expression]
[HAVING group_condition]
[ORDER BY column];
查询平均薪水大于2000的部门 :
分析:该问题实际上是在分组的基础上过滤分组。
SQL> select deptno, avg(sal) ?from emp ?group by deptno ?having avg(sal)>2000;??------注意:having后面不能使用别名
从功能上讲,where和having都是将满足条件的结果进行过滤。但是差别是where子句中不能使用 组函数!所以上句中的having不可以使用where代替。
求10号 部门的平均工资:
分析:在上一条的基础上,having deptno=10; 此时 where也可以做这件事。
? SQL> select deptno, avg(sal) ?from emp ?where deptno=10 group by deptno;?? 因为没有组函数。
在子句中没有使用组函数的情况下,where、having都可以,应该怎么选择?
※SQL优化: 尽量采用where。
如果有分组的话,where是先过滤再分组,而having是先分组再过滤。当数据量庞大如1亿条,where优势明显。
理论基础:——笛卡尔集 ?????
笛卡尔集的行数?=?table1的行数?x?table2的行数
笛卡尔集的列数?=?table1的列数?+?table2的列数
在操作笛卡尔集的时候,应该避免使用“笛卡尔全集”,因为里面含有大量错误信息。?
多表查询就是按照给定条件(连接条件),从笛卡尔全集中选出正确的结果。
根据连接条件的不同可以划分为:等值链接、不等值链接、外链接、自连接
从概念上,区分等值连接和不等值连接非常简单,只需要辨别where子句后面的条件,是“=”为等值连接。不是“=”为不等值连接。
查询员工信息:员工号 姓名 月薪和部门名称
分析:这个问题涉及emp(员工号,姓名,月薪) 和dept(部门名称)两张表 ?——即为多表查询。
通常在进行多表查询的时,会给表起一个别名,使用“别名.列名”的方式来获取数据,直接使用“表名.列名”语法上是允许的,但是实际很少这样用。
如果:select e.empno, e.ename, e.sal, e.deptno, d.dname, d.deptno ?from emp e, dept d;???
直接得到的是笛卡尔全集。其中有错误结果。所以应该加 where 条件进行过滤。
? SQL> ?select e.empno, e.ename, e.sal, d.dname ?from emp e, dept d ?where e.deptno=d.deptno; ?
如果有N个表,where后面的条件至少应该有N-1个。
将上面的问题稍微调整下,查询员工信息:员工号 姓名 月薪 和 薪水级别(salgrade表)
分析:SQL> ?select * from salgrade; ? 看到员工总的薪水级别,共有5级,员工的薪水级别应该满足 >=当前级别的下限,<=该级别的上限:
过滤子句应该: where e.sal >= s.losal and e.sal <= s.hisal
所以: SQL> select e.empno, e.ename, e.sal, s.grade ?from emp e, salgrade s ? ?? where e.sal >= s.losal and e.sal <= s.hisal;
更好的写法应该使用between…and:
SQL> ?select s.grade, e.empno, e.ename, e.sal, e.job ?from emp e, salgrade s ?
where e.sal between s.losal and s.hisal
order by 1 ??
按部门统计员工人数,显示如下信息: 部门号 部门名称 人数
分析:
人数:一定是在emp表中,使用count()函数统计emp表中任一非空列均可。
部门名称:在dept表dname中,直接读取即可。
部门号:任意,两张表都有。
SQL> select d.deptno 部门号, d.dname 部门名称, count(e.empno) 人数 ?from emp e, dept d
?? ?where e.deptno=d.deptno ?group by d.deptno, d.dname ?
注意:由于使用了组函数count(),所以组函数外的d.deptno和d.dname必须放到group?by后。
得到查询结果,但是select * from dept发现40号部门没有显示出来,原因是40号部门没有员工,where没满足。结果不对,40号部门没有员工,应该在40号部门位置显示0。
我们希望: 在最后的结果中,包含某些对于where条件来说不成立的记录?(外链接的作用)
左外链接:当 where e.deptno=d.deptno 不成立的时候,=左边所表示的信息,仍然被包含。
写法:与叫法相反:where e.deptno=d.deptno(+)??
右外链接:当 where e.deptno=d.deptno 不成立的时候,=右边所表示的信息,仍然被包含。
写法:依然与叫法相反:where e.deptno(+)=d.deptno
以上我们希望将没有员工的部门仍然包含到查询的结果当中。因此应该使用外链接的语法。
SQL> select d.deptno 部门号, d.dname 部门名称, count(e.empno) 人数 ?from emp e, dept d
?? ? where e.deptno(+)=d.deptno??group by d.deptno, d.dname; ? 右外链接写法
SQL> select d.deptno 部门号, d.dname 部门名称, count(e.empno) 人数 ?from emp e, dept d
?? ? where d.deptno = e.deptno(+) group by d.deptno, d.dname; 左外链接写法
这样就可以将40号部门包含到整个查询结果中。人数是0
注意:不能使用count(e.*)
核心,通过表的别名,将同一张表视为多张表。
查询员工信息:xxx的老板是 yyy?
分析:执行select * from emp;?发现,员工的老板也在员工表之中,是一张表。要完成多表查询我们可以假设,有两张表,一张表e(emp)只存员工、另一张表b(boss)只存员工的老板。—— from e, b;
老板和员工之间的关系应该是:where ?e.mgr=b.empno??(即:员工表的老板?=?老板表的员工)
SQL> select e.ename || ' ?的老板是 ' ?|| b.ename from emp e, emp b where e.mgr=b.empno??
执行,发现结果正确了,但是KING没有显示出来。KING的老板是他自己。应该怎么显示呢?
SQL> select e.ename?||?' 的老板是 '?|| nvl(b.ename, '他自己' )
??from emp e, emp b
??where e.mgr=b.empno(+) ?
使用concat函数应该怎么做呢???
SQL> select concat( e.ename, concat(' 的老板是 ', ?nvl(b.ename, '他自己' )) )
?from emp e, ?emp b
??where e.mgr = b.empno(+)
?