使用 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 表中有多少行属于每个客户。

客户ID

OrderCount

1234

3

1005

1

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

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

这一次,结果按指定顺序返回:

客户ID

OrderCount

1005

1

1234

3

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

  1. FROM
  2. 在哪里
  3. 按组汇总
  4. SELECT
  5. 排序依据

在 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 的常见障碍是了解出现以下类型的错误消息的原因:

Msg 8120,级别 16,状态 1,第 2 行列 <column_name> 在选择列表中无效,因为它不包含在聚合函数或 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 列添加到 GROUP BY 子句,如下所示:

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

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