【SQL 必知必会】- 第十课 分组数据
目录
数据分组
使用分组可以将数据分为多个逻辑组,对每个组进行聚集计算。
创建分组
分组是使用SELECT 语句的GROUP BY 子句建立的。理解分组的最好办法是看一个例子:
SELECT vend_id, COUNT(*) AS num_prods FROM Products GROUP BY vend_id;
因为使用了GROUP BY,就不必指定要计算和估值的每个组了。系统会自动完成。GROUP BY 子句指示DBMS 分组数据,然后对每个组而不是整个结果集进行聚集。
使用 GROUP BY 的规定:
- GROUP BY 子句可以包含任意数目的列,因而可以对分组进行嵌套,更细致地进行数据分组。
- 如果在GROUP BY 子句中嵌套了分组,数据将在最后指定的分组上进行汇总。换句话说,在建立分组时,指定的所有列都一起计算(所以不能从个别的列取回数据)。
- GROUP BY 子句中列出的每一列都必须是检索列或有效的表达式(但不能是聚集函数)。如果在SELECT 中使用表达式,则必须在GROUP BY子句中指定相同的表达式。不能使用别名。
- 大多数SQL 实现不允许GROUP BY 列带有长度可变的数据类型(如文本或备注型字段)。
- 除聚集计算语句外,SELECT 语句中的每一列都必须在GROUP BY 子句中给出。
- 如果分组列中包含具有NULL 值的行,则NULL 将作为一个分组返回。如果列中有多行NULL 值,它们将分为一组。
- GROUP BY 子句必须出现在WHERE 子句之后,ORDER BY 子句之前。
ALL 子句
Microsoft SQL Server 等有些SQL 实现在GROUP BY 中支持可选的ALL子句。这个子句可用来返回所有分组,即使是没有匹配行的分组也返回(在此情况下,聚集将返回NULL)。具体的DBMS 是否支持ALL,请参阅相应的文档。
过滤分组
我们已经看到了WHERE 子句的作用(第4 课提及)。但是,在这个例子中WHERE 不能完成任务,因为WHERE 过滤指定的是行而不是分组。事实上,WHERE 没有分组的概念。
那么,不使用WHERE 使用什么呢?SQL 为此提供了另一个子句,就是HAVING 子句。HAVING 非常类似于WHERE。事实上,目前为止所学过的所有类型的WHERE 子句都可以用HAVING 来替代。唯一的差别是,WHERE过滤行,而HAVING 过滤分组。
提示:HAVING 支持所有WHERE 操作符
在第4 课和第5 课中,我们学习了WHERE 子句的条件(包括通配符条件和带多个操作符的子句)。学过的这些有关WHERE 的所有技术和选项都适用于HAVING。它们的句法是相同的,只是关键字有差别。
SELECT cust_id, COUNT(*) AS orders FROM Orders GROUP BY cust_id HAVING COUNT(*) >= 2;
在某些情形下,我们需要满足一定条件,同时又要结果个数>1的数据,或者或需要个数≥2的数据,这种情况下使用 having 就很合适。(HAVING COUNT(*) >= 2;)
HAVING 和WHERE 的差别
这里有另一种理解方法,WHERE 在数据分组前进行过滤,HAVING 在数据分组后进行过滤。这是一个重要的区别,WHERE 排除的行不包括在分组中。这可能会改变计算值,从而影响HAVING 子句中基于这些值过滤掉的分组。
那么,有没有在一条语句中同时使用WHERE 和HAVING 子句的需要呢?事实上,确实有。假如想进一步过滤上面的语句,使它返回过去12 个月内具有两个以上订单的顾客。为此,可增加一条WHERE 子句,过滤出过去12 个月内下过的订单,然后再增加HAVING 子句过滤出具有两个以上订单的分组。为了更好地理解,来看下面的例子,它列出具有两个以上产品且其价格大于等于4 的供应商:
SELECT vend_id, COUNT(*) AS num_prods FROM Products WHERE prod_price >= 4 GROUP BY vend_id HAVING COUNT(*) >= 2;
这条语句中,第一行是使用了聚集函数的基本SELECT 语句,很像前面的例子。WHERE 子句过滤所有prod_price 至少为4 的行,然后按vend_id分组数据,HAVING 子句过滤计数为2 或2 以上的分组。如果没有WHERE子句,就会多检索出一行(供应商DLL01,销售4 个产品,价格都在4以下)
SELECT vend_id, COUNT(*) AS num_prods FROM Products GROUP BY vend_id HAVING COUNT(*) >= 2;
使用HAVING 和WHERE
HAVING 与WHERE 非常类似,如果不指定GROUP BY,则大多数DBMS会同等对待它们。不过,你自己要能区分这一点。使用HAVING 时应该结合GROUP BY 子句,而WHERE 子句用于标准的行级过滤。
分组和排序
GROUP BY 和ORDER BY 经常完成相同的工作,但它们非常不同,理解这一点很重要。下表汇总了它们之间的差别。
表中列出的第一项差别极为重要。我们经常发现,用GROUP BY 分组的数据确实是以分组顺序输出的。但并不总是这样,这不是SQL 规范所要求的。此外,即使特定的DBMS 总是按给出的GROUP BY 子句排序数据,用户也可能会要求以不同的顺序排序。就因为你以某种方式分组数据(获得特定的分组聚集值),并不表示你需要以相同的方式排序输出。应该提供明确的ORDER BY 子句,即使其效果等同于GROUP BY 子句。
简单来说就是,GROUP BY会对数据进行分组,但输出的结果不一定会按照升序/降序的方式输出,想要实现升序/降序,请配合使用 Order by子句。
不要忘记ORDER BY
一般在使用GROUP BY 子句时,应该也给出ORDER BY 子句。这是保证数据正确排序的唯一方法。千万不要仅依赖GROUP BY 排序数据。
为说明GROUP BY 和ORDER BY 的使用方法,来看一个例子。下面的SELECT语句类似于前面那些例子。它检索包含三个或更多物品的订单号和订购物品的数目:
SELECT order_num, COUNT(*) AS items FROM OrderItems GROUP BY order_num HAVING COUNT(*) >= 3;
要按订购物品的数目排序输出,需要添加ORDER BY 子句,如下所示:
SELECT order_num, COUNT(*) AS items FROM OrderItems GROUP BY order_num HAVING COUNT(*) >= 3 ORDER BY items, order_num;
order_num items
--------- -----
20009 3
20007 5
20008 5
在这个例子中,使用GROUP BY 子句按订单号(order_num 列)分组数据,以便COUNT(*)函数能够返回每个订单中的物品数目。HAVING 子句过滤数据,使得只返回包含三个或更多物品的订单。最后,用ORDER BY子句排序输出。
SELECT 子句顺序
下面回顾一下SELECT 语句中子句的顺序。下表以在SELECT 语句中使用时必须遵循的次序,列出迄今为止所学过的子句。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)