9.3 使用HAVING子句包含或者排除分组行

创建数据组和应用聚合函数非常有用。细化这些特性就是依据组级条件包含或者排除结果。本节将介绍HAVING子句。WHERE子句和HAVING子句之间有明显的区别。

9.3.1 限制分组结果

WHERE子句条件限制查询返回的行。依据其是否满足列出的条件来包含一些行,这些行有时称为行级结果。使用GROUP BY子句群集行并且将聚合函数应用于这些组,这样返回的结果通常称为组级结果。HAVING子句用来限制组级结果。

下面的查询依据DEPARTMENT_ID列值指定WHERE条件,从而限制从JOB_HISTORY表检索的行:

        select department_id
        from job_history
        where department_id in (50,60,80,110);

这个查询返回7行。如果没有WHERE子句,就会检索全部10行。假设要知道以前这些部门雇用了多少名员工,可以手动分组和计数这7行。然而,如果有许多行,那么可以使用聚合函数(例如COUNT),如下面的查询所示:

        select department_id, count(*)
        from job_history
        where department_id in (50,60,80,110)
        group by department_id;

这个查询非常类似于前面的语句。将聚合函数COUNT添加到SELECT列表,同时添加group by department_id子句。返回4行,包括它们的聚集行数。很明显,最初由WHERE子句限制的7行依据公共的DEPARTMENT_ID值群集为4组,如表9-1所示。

表9-1 依据DEPARTMENT_ID值分为4组

假设要限制该列表,使其只包含有多名员工的那些部门。HAVING子句按要求限制或者限定组级行。

该查询必须执行下面几个步骤:

(1) 考察整个行级数据集。

(2) 依据所有WHERE子句条件限制数据集。

(3) 使用GROUP BY子句中指定的分组特性将数据划分为一个或者多个组。

(4) 应用任何聚合函数,创建一个新的组级数据集。依据创建的组,每一行都可看作其行级源数据的聚集。

(5) 使用HAVING子句条件限制或者限定组级数据,只返回匹配这些条件的组级结果。

注意:

依据限制物理行还是组级行,选择相应的上下文来使用WHERE或者HAVING子句。当限制实际(物理)行时,使用WHERE子句强加一个或者多个条件。分组这些行时,可以使用一个或者多个聚合函数,生成一个或者多个组级行。使用HAVING子句限制组级行。

9.3.2 HAVING子句

添加HAVING子句之后,SELECT语句的一般形式得到了进一步增强,如下所示:

        SELECT column|expression|group_function(column|expression [alias]), ...}
        FROM table
        [WHERE condition(s)]
        [GROUP BY {col(s)|expr}]
        [HAVING group_condition(s)]
        [ORDER BY {col(s)|expr|numeric_pos} [ASC|DESC] [NULLS FIRST|LAST]];

HAVING子句与SELECT语句的其他子句之间的重要区别是,只有存在GROUP BY子句才能指定HAVING子句。这种依赖性显而易见,因为在限制组级行之前这些行必须存在。在SELECT语句中,HAVING子句可能出现在GROUP BY子句之前。然而,通常将HAVING子句放在GROUP BY子句之后。在对HAVING子句求值之前,先实现所有分组并执行分组函数。

下面的查询显示如何使用HAVING子句限制聚集的数据集。JOB_HISTORY表的记录被划分为4组。返回满足HAVING子句条件(将多行添加到组行计数)的所有行:

        select department_id, count(*)
        from job_history
        where department_id in (50,60,80,110)
        group by department_id
        having count(*)>1;

返回DEPARTMENT_ID值为50、80和110的3行,每一行的COUNT(*)值分别为2、2、3。

图9-7显示了三个查询。查询1将EMPLOYEES表中的107条记录按照公共JOB_ID值划分为19个组,并计算各个JOB_ID组的平均薪水和聚集行数。查询2使用HAVING子句,有条件地排除平均薪水少于或等于12 000的聚集行,从而细化结果。查询3说明了可以使用布尔运算符来指定多个HAVING子句条件。

图9-7 HAVING子句

考点:

只有当存在GROUP BY子句时才能指定HAVING子句。可以在没有HAVING子句的情况下指定GROUP BY子句。

练习9-3 使用HAVING子句

公司计划招聘活动,需要确定雇佣了20个或者更多员工的日期是星期几。要求报表中列出这些日期以及在这些日期中每天雇佣的员工数。

(1) 启动SQL*Plus或SQL Developer,连接到HR模式。

(2) EMPLOYEES记录必须依据HIRE_DATE列的日组成部分划分为多个组。使用COUNT函数可以得到每个组中的员工数。使用HAVING子句将这7行限制为计数大于或者等于20的那些行。

(3) 可能的解决方案是下列语句,返回雇佣20名或者更多员工的日期是星期几。

        select to_char(hire_date, 'Day') hire_day, count(*)
        from employees
        group by to_char(hire_date, 'Day')
        having count(*)>=20;

9.4 本章知识点回顾

描述分组函数

● 分组函数也称为多行函数、聚合函数或者汇总函数。它们对每组数据执行一次,并将来自多行的数据聚集为各组的单个结果。

● 组可以是整个表或者通过公共分组特性集合的表的多个部分。

● 对列或表达式执行的COUNT函数返回表示组中行数的整数值,其中指定的列或表达式非空。

● SUM函数返回组中所有非空数值表达式值的聚集和。

● AVG函数将列或者表达式的和除以组中的非空行数。

● MAX和MIN函数作用于NUMBER、DATE、CHAR和VARCHAR2数据类型,它们返回组中最大项或者最小项的值。

使用GROUP BY子句分组数据

● GROUP BY子句指定分组特性,行必须共同具有该特性以便将它们分组。

● GROUP BY子句帮助在选中的数据集中创建组,它出现在WHERE子句之后、ORDER BY子句之前。

● SELECT列表上不是分组函数的任何项必定是分组特性。

● WHERE子句中不能有分组函数。

● 依据多个分组特性,可以将数据集划分为组并且进一步划分为子组。

● LISTAGG函数返回排序后的列值的连接字符串,排序由WITHIN GROUP子句后面的ORDER BY表达式指定。

使用HAVING子句包含或者排除分组行

● 使用公共分组特性和GROUP BY子句群集行,并将聚合函数应用于这些组,就会返回组级结果。

● HAVING子句提供语言来限制返回的组级结果。

● 只有当存在GROUP BY子句时,才能指定HAVING子句。

● 在对HAVING子句求值之前实现所有分组并执行分组函数。