10.3 过滤分组

除了能用GROUP BY分组数据外,SQL还允许过滤分组,规定包括哪些分组,排除哪些分组。例如,你可能想要列出至少有两个订单的所有顾客。为此,必须基于完整的分组而不是个别的行进行过滤。

我们已经看到了WHERE子句的作用(第4课提及)。但是,在这个例子中WHERE不能完成任务,因为WHERE过滤指定的是行而不是分组。事实上,WHERE没有分组的概念。

那么,不使用WHERE使用什么呢?SQL为此提供了另一个子句,就是HAVING子句。HAVING非常类似于WHERE。事实上,目前为止所学过的所有类型的WHERE子句都可以用HAVING来替代。唯一的差别是,WHERE过滤行,而HAVING过滤分组。

提示:HAVING支持所有WHERE操作符
在第4课和第5课中,我们学习了WHERE子句的条件(包括通配符条件和带多个操作符的子句)。学过的这些有关WHERE的所有技术和选项都适用于HAVING。它们的句法是相同的,只是关键字有差别。

那么,怎么过滤分组呢?请看以下的例子:

输入▼

  1. SELECT cust_id, COUNT(*) AS orders
  2. FROM Orders
  3. GROUP BY cust_id
  4. HAVING COUNT(*) >= 2;

输出▼

  1. cust_id orders
  2. ---------- -----------
  3. 1000000001 2

分析▼

这条SELECT语句的前三行类似于上面的语句。最后一行增加了HAVING子句,它过滤COUNT(*) >= 2(两个以上订单)的那些分组。

可以看到,WHERE子句在这里不起作用,因为过滤是基于分组聚集值,而不是特定行的值。

说明:HAVINGWHERE的差别
这里有另一种理解方法,WHERE在数据分组前进行过滤,HAVING在数据分组后进行过滤。这是一个重要的区别,WHERE排除的行不包括在分组中。这可能会改变计算值,从而影响HAVING子句中基于这些值过滤掉的分组。

那么,有没有在一条语句中同时使用WHEREHAVING子句的需要呢?事实上,确实有。假如想进一步过滤上面的语句,使它返回过去12个月内具有两个以上订单的顾客。为此,可增加一条WHERE子句,过滤出过去12个月内下过的订单,然后再增加HAVING子句过滤出具有两个以上订单的分组。

为了更好地理解,来看下面的例子,它列出具有两个以上产品且其价格大于等于4的供应商:

输入▼

  1. SELECT vend_id, COUNT(*) AS num_prods
  2. FROM Products
  3. WHERE prod_price >= 4
  4. GROUP BY vend_id
  5. HAVING COUNT(*) >= 2;

输出▼

  1. vend_id num_prods
  2. ------- -----------
  3. BRS01 3
  4. FNG01 2

分析▼

这条语句中,第一行是使用了聚集函数的基本SELECT语句,很像前面的例子。WHERE子句过滤所有prod_price至少为4的行,然后按vend_id分组数据,HAVING子句过滤计数为2或2以上的分组。如果没有WHERE子句,就会多检索出一行(供应商DLL01,销售4个产品,价格都在4以下):

输入▼

  1. SELECT vend_id, COUNT(*) AS num_prods
  2. FROM Products
  3. GROUP BY vend_id
  4. HAVING COUNT(*) >= 2;

输出▼

  1. vend_id num_prods
  2. ------- -----------
  3. BRS01 3
  4. DLL01 4
  5. FNG01 2

说明:使用HAVINGWHERE
HAVINGWHERE非常类似,如果不指定GROUP BY,则大多数DBMS会同等对待它们。不过,你自己要能区分这一点。使用HAVING时应该结合GROUP BY子句,而WHERE子句用于标准的行级过滤。