使用 GROUP BY 汇总数据

已完成

虽然聚合函数可用于分析,但你可能希望在汇总数据之前将数据排列成子集。 在本机中,你将学习如何使用 GROUP BY 子句完成此操作。

使用 GROUP BY 子句

如你所知,处理 SELECT 语句时,在对 FROM 子句和 WHERE 子句求值后,将创建一个虚拟表。 虚拟表的内容现在可供进一步处理。 可使用 GROUP BY 子句将此虚拟表的内容细分为行组。

要对行进行分组,请在 GROUP BY 子句中指定一个或多个元素:

GROUP BY <value1> [, <value2>, …]

GROUP BY 根据子句中指定的元素创建组并将行放入每个组中。

例如,以下查询将生成一组分组行,Sales.SalesOrderHeader 表中每个 CustomerID 对应一行。 查看 GROUP BY 进程的另一种方法是,与 CustomerID 值相同的所有行都将分组在一起,并在单个结果行中返回。

SELECT CustomerID
FROM Sales.SalesOrderHeader
GROUP BY CustomerID;

上面的查询等效于以下查询:

SELECT DISTINCT CustomerID
FROM Sales.SalesOrderHeader

处理 GROUP BY 子句并将每一行与某个组关联后,查询的后续阶段必须聚合源行中满足以下条件的所有元素:位于 SELECT 列表中,但未出现在 GROUP BY 列表中。 此要求会影响你编写 SELECT 子句和 HAVING 子句的方式。

那么,用 GROUP BY 或 DISTINCT 编写查询有什么区别? 如果只是想知道 CustomerID 的非重复值,则没有任何区别。 但使用 GROUP BY 时,我们可以将其他元素添加到 SELECT 列表中,随后对每个元素进行聚合。

最简单的聚合函数是 COUNT(*)。 下面的查询从 CustomerID 获取原始的 830 个源行,并根据 CustomerID 值将它们分为 89 个组。 每个不同的 CustomerID 值在 GROUP BY 查询中生成一行输出

SELECT CustomerID, COUNT(*) AS OrderCount
FROM Sales.SalesOrderHeader
GROUP BY CustomerID;

对于每个 CustomerID 值,查询聚合行并进行计数,因此结果显示 SalesOrderHeader 表中有多少行属于每个客户。

CustomerID

OrderCount

1234

3

1005

1

请注意,GROUP BY 无法保证结果的顺序。 通常,这是由查询处理器执行分组操作的结果,将按照组值的顺序返回结果。 但不能依赖于这一行为。 如果需要对结果进行排序,则必须显式包含 ORDER 子句:

SELECT CustomerID, COUNT(*) AS OrderCount
FROM Sales.SalesOrderHeader
GROUP BY CustomerID
ORDER BY CustomerID;

此时,按指定顺序返回结果:

CustomerID

OrderCount

1005

1

1234

3

按以下顺序应用 SELECT 语句中的子句:

  1. FROM
  2. WHERE
  3. GROUP BY
  4. HAVING
  5. SELECT
  6. ORDER BY

在 SELECT 子句中分配列的别名,别名在 GROUP BY 子句之后,但在 ORDER BY 子句之前。 可以在 ORDER BY 子句中引用列的别名,但不能在 GROUP BY 子句中引用列的别名。 以下查询将导致“列名称无效”错误:

SELECT CustomerID AS Customer,
       COUNT(*) AS OrderCount
FROM Sales.SalesOrderHeader
GROUP BY Customer
ORDER BY Customer;

但是,以下查询将会成功,并按客户 ID 对结果进行分组和排序。

SELECT CustomerID AS Customer,
       COUNT(*) AS OrderCount
FROM Sales.SalesOrderHeader
GROUP BY CustomerID
ORDER BY Customer;

GROUP BY 错误故障排除

要熟练运用 SELECT 语句中的 GROUP BY,一个常见障碍是理解下列类型的错误消息的出现原因:

消息 8120,级别 16,状态 1,第 2 行列 column_name 在 SELECT 列表中无效,因为它既不包含在聚合函数中,也不包含在 GROUP BY 子句中。

例如,允许以下查询,因为 SELECT 列表中的每一列都是 GROUP BY 子句中的列,或者是对每个组操作的聚合函数:

SELECT CustomerID, COUNT(*) AS OrderCount
FROM Sales.SalesOrderHeader
GROUP BY CustomerID;

以下查询将返回错误,因为 PurchaseOrderNumber 不是 GROUP BY 的一部分,并且不能与聚合函数一起使用。

SELECT CustomerID, PurchaseOrderNumber, COUNT(*) AS OrderCount
FROM Sales.SalesOrderHeader
GROUP BY CustomerID;

此查询返回以下错误:

Msg 8120, Level 16, State 1, Line 1
Column 'Sales.SalesOrderHeader.PurchaseOrderNumber' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.

下面是另一种思考方法。 此查询针对每个 CustomerID 值返回一行。 但同一 CustomerID 的行可以有不同的 PurchaseOrderNumber 值,那么应该返回哪个值呢?

如果要查看每个客户 ID 和每个采购订单的订单,则可以将 PurchaseOrderNumber 列添加到 GROUPBY 子句中,如下所示:

SELECT CustomerID, PurchaseOrderNumber, COUNT(*) AS OrderCount
FROM Sales.SalesOrderHeader
GROUP BY CustomerID, PurchaseOrderNumber;

此查询将针对每个客户和每个采购订单组合返回一行,以及该组合的订单计数。