SQL 组合使用外连接查询与聚合函数

SQL 组合使用外连接查询与聚合函数,略微修改了 EMP_BONUS 表的数据,使得部门编号为 10 的员工中只有部分人获得了奖金。考虑如下所示的 EMP_BONUS 表和查询语句,该查询(表面上)计算出了部门编号为 10 的员工的工资总额和奖金总额。

SQL 组合使用外连接查询与聚合函数 问题描述

略微修改了 EMP_BONUS 表的数据,使得部门编号为 10 的员工中只有部分人获得了奖金。考虑如下所示的 EMP_BONUS 表和查询语句,该查询(表面上)计算出了部门编号为 10 的员工的工资总额和奖金总额。

select * from emp_bonus
 
     EMPNO RECEIVED          TYPE
---------- ----------- ----------
      7934 17-MAR-2005          1
      7934 15-FEB-2005          2
 
select deptno,
       sum(sal) as total_sal,
       sum(bonus) as total_bonus
  from (
select e.empno,
       e.ename,
       e.sal,
       e.deptno,
       e.sal*case when eb.type = 1 then .1
                  when eb.type = 2 then .2
                  else .3 end as bonus
  from emp e, emp_bonus eb
 where e.empno = eb.empno
   and e.deptno = 10
       )
 group by deptno
 
DEPTNO  TOTAL_SAL TOTAL_BONUS
------ ---------- -----------
    10       2600         390

奖金总额的结果是正确的,但工资总额却不是部门编号为 10 的员工的工资总额。下面的查询语句解释了为什么工资总额不正确。

select e.empno,
       e.ename,
       e.sal,
       e.deptno,
       e.sal*case when eb.type = 1 then .1
                  when eb.type = 2 then .2
       else .3 end as bonus
  from emp e, emp_bonus eb
 where e.empno = eb.empno
   and e.deptno = 10
 
   EMPNO ENAME             SAL     DEPTNO      BONUS
-------- ---------- ---------- ---------- ----------
    7934 MILLER           1300         10        130
    7934 MILLER           1300         10        260

上述查询没有计算部门编号为 10 的全部员工的工资总额,实际上只有 MILLER 的工资被计入总和,而且被错误地计算了两次。其实,你最终想得到如下所示的结果集。

DEPTNO  TOTAL_SAL TOTAL_BONUS
------ ---------- -----------
    10       8750         390

SQL 组合使用外连接查询与聚合函数 解决方案

下面的解决方案也和 3.9 节的类似,不同之处在于要外连接 EMP_BONUS 表,确保把部门编号为 10 的全部员工都包括进来。
DB2、MySQLPostgreSQLSQL Server
外连接 EMP_BONUS 表,然后去掉部门编号为 10 的员工的重复项,再计算工资总和。

 1  select deptno,
 2         sum(distinct sal) as total_sal,
 3         sum(bonus) as total_bonus
 4    from (
 5  select e.empno,
 6         e.ename,
 7         e.sal,
 8         e.deptno,
 9         e.sal*case when eb.type is null then 0
10                    when eb.type = 1 then .1
11                    when eb.type = 2 then .2
12                    else .3 end as bonus
13    from emp e left outer join emp_bonus eb
14      on (e.empno = eb.empno)
15   where e.deptno = 10
16         )
17   group by deptno

也可以使用窗口函数 SUM OVER

 1  select distinct deptno,total_sal,total_bonus
 2    from (
 3  select e.empno,
 4         e.ename,
 5         sum(distinct e.sal) over
 6         (partition by e.deptno) as total_sal,
 7         e.deptno,
 8         sum(e.sal*case when eb.type is null then 0
 9                        when eb.type = 1 then .1
10                        when eb.type = 2 then .2
11                        else .3
12                   end) over
13         (partition by deptno) as total_bonus
14    from emp e left outer join emp_bonus eb
15      on (e.empno = eb.empno)
16   where e.deptno = 10
17         ) x

Oracle
对于 Oracle 9i 数据库及其后续版本,上述解决方案仍然适用。除此之外,我们也可以使用 Oracle 专有的外连接语法。对于 Oracle 8i 数据库及更早的版本,只能使用该语法实现外连接。

 1  select deptno,
 2         sum(distinct sal) as total_sal,
 3         sum(bonus) as total_bonus
 4    from (
 5  select e.empno,
 6         e.ename,
 7         e.sal,
 8         e.deptno,
 9         e.sal*case when eb.type is null then 0
10                    when eb.type = 1 then .1
11                    when eb.type = 2 then .2
12                    else .3 end as bonus
13    from emp e, emp_bonus eb
14   where e.empno  = eb.empno (+)
15     and e.deptno = 10
16         )
17   group by deptno

与 DB2 及其他数据库类似,Oracle 8i 数据库也支持 SUM OVER 语法,但必须把上面的查询语句里出现的外连接改为 Oracle 专有的语法。

SQL 组合使用外连接查询与聚合函数 扩展知识

本实例“问题”部分中的第二个查询语句连接了 EMP 表和 EMP_BONUS 表,却只返回了员工 MILLER 的两行数据,这是导致 EMP 表的工资总额计算出错的原因(部门编号为 10 的其他员工没有奖金,他们的工资没有被计入总和)。解决办法则是把 EMP 表外连接到 EMP_BONUS 表,这样一来,那些没有奖金的员工也会被计算进来。如果一个员工没有奖金,那么 EMP_BONUS 表中的 TYPE 就是 Null 值。注意到这一点非常重要,因为 CASE 语句部分已经在 3.9 节的基础上稍微有了变动。如果 EMP_BONUS 表中的 TYPENull 值,则 CASE 表达式会返回 0,这样就不会对总和产生影响。
下面的查询语句是另一种解决方案。首先计算部门编号为 10 的员工的工资总额,然后再连接 EMP 表和 EMP_BONUS 表(这样就避免了使用外连接)。下面的查询语句适用于所有的关系数据库管理系统。

select d.deptno,
       d.total_sal,
       sum(e.sal*case when eb.type = 1 then .1
                      when eb.type = 2 then .2
                      else .3 end) as total_bonus
   from emp e,
        emp_bonus eb,
        (
 select deptno, sum(sal) as total_sal
   from emp
  where deptno = 10
  group by deptno
        ) d
  where e.deptno = d.deptno
     and e.empno = eb.empno
   group by d.deptno,d.total_sal
 
   DEPTNO  TOTAL_SAL  TOTAL_BONUS
--------- ---------- -----------
       10       8750         390

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程