SQL 组合使用连接查询与聚合函数,你想执行一个聚合操作,但查询语句涉及多个表。你希望确保表之间的连接查询不会干扰聚合操作。例如,你希望计算部门编号为 10
的员工的工资总额以及奖金总和。因为有部分员工多次获得奖金,所以在 EMP
表和 EMP_BONUS
表连接之后再执行聚合函数 SUM
,就会得出错误的计算结果。在这个问题中,EMP_BONUS
表里有如下数据。
SQL 组合使用连接查询与聚合函数 问题描述
你想执行一个聚合操作,但查询语句涉及多个表。你希望确保表之间的连接查询不会干扰聚合操作。例如,你希望计算部门编号为 10
的员工的工资总额以及奖金总和。因为有部分员工多次获得奖金,所以在 EMP
表和 EMP_BONUS
表连接之后再执行聚合函数 SUM
,就会得出错误的计算结果。在这个问题中,EMP_BONUS
表里有如下数据。
现在,考虑下面的查询语句,它返回了部门编号为 10 的所有员工的工资和奖金。BONUS
表中的 TYPE
列决定了奖金的数额。若 TYPE
值等于 1,则奖金为工资的 10%;若 TYPE
值等于 2,则奖金为工资的 20%;若 TYPE
值等于 3,则奖金为工资的 30%。
到目前为止,一切都很顺利。然而,如果你试图连接 EMP_BONUS
表并计算奖金总和,就会出错。
尽管奖金总额(TOTAL_BONUS
)是正确的,但工资总额(TOTAL_SAL
)却是错误的。部门编号为 10 的所有员工的工资总额应该是 8750,如下所示。
为什么工资总额不对呢?这是因为连接查询导致某些行的 SAL
列出现了两次。考虑下面连接 EMP
表和 EMP_BONUS
表的查询语句。
现在就能很容易地看出来为什么工资总额是错误的了,因为 MILLER 的工资被统计了两次。你真正想要的结果集应该如下所示。
SQL 组合使用连接查询与聚合函数 解决方案
在连接查询里进行聚合运算时,必须十分小心才行。如果连接查询产生了重复行,通常有两种办法来使用聚合函数,而且可以避免得出错误的计算结果。一种方法是,调用聚合函数时直接使用关键字 DISTINCT
,这样每个值都会先去掉重复项再参与计算;另一种方法是,在进行连接查询之前先执行聚合运算(以内嵌视图的方式),这样可以避免错误的结果,因为聚合运算发生在连接查询之前。下面的解决方案使用了 DISTINCT
。之后,我们将讨论在连接查询之前使用内嵌视图执行聚合运算的做法。
MySQL 和 PostgreSQL
使用 DISTINCT
计算工资总额。
DB2、Oracle 和 SQL Server
上述解决方案也适用于这些数据库。另外,它们还支持窗口函数 SUM OVER
。
SQL 组合使用连接查询与聚合函数 扩展知识
本实例“问题”部分的第二个查询语句把 EMP
表和 EMP_BONUS
表连接起来,并返回了员工 MILLER 的两行数据,这是导致 EMP
表的工资总额出错的原因(MILLER 的工资被加了两次)。对应的解决办法是只计算不同的 EMP.SAL
值。下面的查询语句是另一种解决方案。首先计算部门编号为 10 的全部员工的工资总额,然后连接 EMP
表和 EMP_BONUS
表。下面的查询语句适用于所有的关系数据库管理系统。
DB2、Oracle 和 SQL Server
上面的另一种解决方案利用了窗口函数 SUM OVER
。下面的查询语句来自该解决方案的第 3 行至第 14 行,返回的结果集如下。
窗口函数 SUM OVER
被调用了两次,第一次调用针对指定的分区或者分组计算工资总额。在本例中,分区指的是编号为 10 的部门,该部门员工的工资总额是 8750。第二次调用 SUM OVER 针对同一个分区计算奖金总额。最终的结果集则是在去除了 TOTAL_SAL
、DEPTNO
以及 TOTAL_BONUS
组合的重复项之后产生的。