SQL SUM 函数

SQL Server 中的 SUM() 函数是聚合函数之一,用于计算所选所有数字记录(或行)的总数或总和。它接受一个参数,并将该列中的所有行或值相加,然后返回总数。例如,SUM() 函数用于计算总销售额、总收入、支出或订单价值等。

SQL SUM() 函数是在处理或评估销售、利润、生产、财务或其他任何数值计算时最重要的函数之一。如前所述,如果在 SELECT 语句中将其用于单个列,它将返回单个结果。但是,它通常与 GROUP BY 子句以及可选的 WHERE 和 HAVING 子句结合使用以进行筛选。如果您的 SELECT 语句包含多个列,则必须使用 GROUP BY 子句来对非 SUM() 函数的列进行分组。此外,您可以在 SUM() 函数中使用 DISTINCT 关键字来计算唯一行的总和。

SQL SUM 函数语法

用于查找数字总和的 SUM() 函数的语法如下所示。

SELECT SUM([Column_Name])
FROM [Source]

有点复杂,它使用了 GROUP BY、WHERE 和 HAVING 子句。

SELECT column1, SUM([ALL | DISTINCT] expression)
FROM [Source]
WHERE <condition>
GROUP BY column1
HAVING SUM(ColumnName) > expression

由于它用于查找总数,因此我们必须在数值数据类型上使用 SUM() 函数,例如 INT、FLOAT、DECIMAL、MONEY 等。如果您将其应用于 VARCHAR 或任何其他字符串文本,它将引发错误。我们可以在 SQL Server SUM() 函数中使用的项目列表是:

  • SUM(column_name):您想查找总数的任何数值列。SUM() 外部的任何列都应放在 GROUP BY 中或放在另一个聚合函数中。
  • ALL:这是默认值,适用于所有列。
  • DISTINCT:用于查找唯一行的总和。
  • Source:从其中检索数据的表名。
  • Condition:这是可选的。如果在聚合 Sum() 之前,您想过滤数据,请使用它。
  • GROUP BY:同样是可选的,但您必须将非聚合列(不在 SUM() 中)放在此子句中。

除了上述简单用法之外,以下 SQL SUM() 函数语法是最先进的,并且用于分析。

SUM([ALL] expression) OVER ([Partition_by] Order_by)

我们将提供详细的解释和简单的示例来理解 OVER 和 PARTITION BY 子句。

为了给您一个背景,如果您想查找商店中产品的总价。如果您想查找商店中黑色产品的总价,您可以使用此方法。本页讨论的示例列表使用 AdventureWorksDW 2022 数据库表。

需要记住的关键点

  • 始终使用数值列,如 INTEGER、DECIMAL、FLOAT 等列作为 SUM() 函数的参数。否则,它将引发错误。例如,SUM(FullName) 将返回错误。
  • SQL SUM() 函数中未包含的任何列都应在 GROUP BY 子句中提及。
  • 您选择的列必须与 GROUP BY 中放置的列匹配。
  • 使用 WHERE 子句在 SUM() 函数应用于行之前过滤数据。
  • 要过滤 SUM() 函数数据,请使用 HAVING 子句,因为 WHERE 子句无效。
  • SUM() 函数会自动忽略 NULL 值。
  • 仅在真正需要唯一总计时才使用 DISTINCT 关键字。
  • 在对大数值使用 SUM() 函数时,处理小数精度非常重要。因此,在 SUM() 之前使用 ROUND() 函数。例如,SELECT ROUND(SUM(Sales), 2)) AS TotalSale FROM Fact。

SQL SUM 函数应用于单列

如前所述,SUM() 函数返回指定列中记录总数的聚合值。在此示例中,我们将使用 SUM() 函数计算总销售额。为此,我们使用 AdventureWorksDW 2022 数据库中的 FactInternetSales 表。

提示:您必须始终使用别名列为 SUM() 函数的输出提供有意义的名称。

SELECT SUM(SalesAmount) AS TotalSales
FROM FactInternetSales

上面的查询将 SalesAmount 用作 SUM() 函数的参数,并计算 InternetSales 表中销售额的总和。由于这是一个聚合值,因此结果集将是单个值。

TotalSales
-----------
29358677.2207

如何查找多列的总和?

在 SQL Server 中,您不仅限于在单个列上使用 SUM 函数。我们可以在单个 SELECT 语句中使用它来查找多个值的总和。在此示例中,我们查找销售额、总产品销售额和订单的总和。

SELECT SUM(TotalProductCost) AS Cost,
SUM(SalesAmount) AS Sale,
SUM(OrderQuantity) AS Orders
FROM FactInternetSales

结果是

Cost	                        Sale	                       Orders
----------------         ----------------       --------
17277793.5757	29358677.2207	60398

SQL SUM() 函数和 GROUP BY

在前面的示例中,我们使用 SUM() 函数查找单个列的聚合总和。在实际操作中,我们需要计算分组内数值的总和。但是,如果您尝试在 SELECT 语句中组合聚合列和非聚合列,那么服务器将引发错误。为了解决此错误,您必须使用 GROUP BY 子句来组合这些非聚合列。

在大多数情况下,我们通常使用此 sum 函数来查找属于特定类别或颜色的总产品销售额。以下是一些使用 SUM() 和 GROUP BY 组合的实例:

  • 某个产品类别和子类别的销售总额。
  • 每个国家、地区、州等的总销售额。
  • 查找年度、月度或季度销售额。

以下查询使用 YEAR() 函数从 Order date 获取年份数字,GROUP BY 子句将按年份对日期进行分组。接下来,SUM() 函数查找每年的总销售额。

SELECT YEAR(OrderDate) AS Year, SUM(SalesAmount) AS Sales
FROM FactInternetSales
GROUP BY YEAR(OrderDate)

错误及修复

在使用 SQL Server SUM() 函数时,最常见的错误之一是没有在 GROUP BY 中提及非聚合列。例如,下面的查询包含 Year 列,但未在分组中提及。因此,它将引发错误。要修复此错误,请在上面的查询中添加最后一行。

SELECT YEAR(OrderDate) AS Year, SUM(SalesAmount) AS Sales
FROM FactInternetSales
Error: Column 'FactInternetSales.OrderDate' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.

使用 SUM() 和 JOIN

除了处理单个表之外,您还可以使用任何 SQL JOIN 来计算来自多个表的聚合 SUM()。这非常有帮助,并且可以为结果集提供更多信息。例如,按区域组计算销售总额。为此,我们需要一个包含销售信息的表,以及另一个包含区域详细信息的表。在这种情况下,请使用 JOIN 来连接这两个表,并使用 SQL SUM() 函数按组计算总销售额。

SELECT [SalesTerritoryGroup], SUM(SalesAmount) AS Sales
FROM [DimSalesTerritory] st JOIN FactInternetSales fs
ON st.SalesTerritoryKey = fs.SalesTerritoryKey
GROUP BY [SalesTerritoryGroup]

正如我们在 GROUP BY 文章中详细解释的那样,您可以在执行 SUM() 函数时使用单个值或多个值。下面的查询按区域组和国家计算销售额和订单的总和。

SELECT [SalesTerritoryGroup], [SalesTerritoryCountry], 
SUM(SalesAmount) AS Sales, SUM(OrderQuantity) AS Orders
FROM [DimSalesTerritory] st JOIN FactInternetSales fs
ON st.SalesTerritoryKey = fs.SalesTerritoryKey
GROUP BY [SalesTerritoryGroup], [SalesTerritoryCountry]

使用 SQL SUM() 函数和 DISTINCT

默认情况下,SUM() 函数使用 ALL 作为默认值,并查找给定列中所有行的总和。但是,您可以显式指定 DISTINCT 关键字来获取唯一记录的总和。它首先在 SUM() 函数计算总数之前删除重复行。

在下面的查询中,我们使用了常规的 DISTINCT SUM() 来显示在计算总数时两者之间的区别。此处,DISTINCT SalesAmount 计算 FactInternetSales 表中不为 NULL 的唯一行的总和(忽略 NULL 记录)。

SELECT SUM(SalesAmount) AS Sale, SUM(DISTINCT SalesAmount) AS UniqueSale
FROM FactInternetSales

结果是

销售额唯一销售额
29358677.220736097.586

我再举一个 DISTINCT SUM() 函数与分组的例子,以便您可以看到分组的常规和唯一行计算。下面的查询按年份对行进行分组,并查找每个年份组中所有销售记录的总和。还有每个组中不为 NULL 的唯一销售额。

SELECT YEAR(OrderDate) AS Year, SUM(SalesAmount) AS Sale, 
SUM(DISTINCT SalesAmount) AS UniqueSale
FROM FactInternetSales
GROUP BY YEAR(OrderDate)

提示:在上述查询的顶部,您可以使用 WHERE 子句来限制或过滤 DISTINCT SUM()。

SQL SUM() 函数和 HAVING 子句

常规的 WHERE 子句不能用于聚合的 SUM() 数据,因此我们必须使用 HAVING 子句。当您将 HAVING 子句与 SUM() 函数一起使用时,它会对聚合的总数应用过滤器。下面的查询按 ProductKey 对 Fact 表进行分组。HAVING 子句检查 SUM() 函数返回的聚合金额(销售金额的总和)是否大于 1000000。如果为 True,则将显示相应的记录。

SELECT ProductKey, SUM(SalesAmount) AS Sale
FROM FactInternetSales
GROUP BY ProductKey
HAVING SUM(SalesAmount) > 1000000
SQL SUM Function Example

SQL Server SUM 函数 WHERE 子句

聚合的 SUM() 函数可以与 WHERE 子句结合使用以过滤数据。如果您使用 WHERE 子句,它会在应用 SUM() 函数之前过滤行。通过这种方式,您可以从 SUM() 函数中排除不需要的列来计算总数。例如,计算除黑色、白色和蓝色产品外的总产品销售额。

下面的查询将 WHERE Clause 与 SUM() 函数一起使用,以显示除东北、东南和德国以外的每个 SalesTerritoryRegion 的销售金额总和。如果您需要对聚合数据进行进一步过滤,可以取消注释 HAVING Clause(最后一行)以显示总订单数大于 6000 的记录。

SELECT [SalesTerritoryRegion], 
SUM(SalesAmount) AS Sales, SUM(OrderQuantity) AS Orders
FROM [DimSalesTerritory] st JOIN FactInternetSales fs
ON st.SalesTerritoryKey = fs.SalesTerritoryKey
WHERE [SalesTerritoryRegion] NOT IN ('Northeast', 'Southeast', 'Germany')
GROUP BY [SalesTerritoryRegion]
--HAVING SUM(OrderQuantity) > 6000

将 SUM 与 ORDER BY 子句一起使用

您可以在 ORDER BY 子句中使用此 SQL Server SUM 函数。例如,下面的查询将按 Region 列分组查找总订单和销售额。接下来,ORDER BY Clause 将根据销售额的降序对这些查询结果进行排序。这里,ORDER BY 识别了别名列名,因此我们使用了它。

SELECT [SalesTerritoryRegion], 
SUM(SalesAmount) AS Sales, SUM(OrderQuantity) AS Orders
FROM [DimSalesTerritory] st JOIN FactInternetSales fs
ON st.SalesTerritoryKey = fs.SalesTerritoryKey
WHERE [SalesTerritoryRegion] NOT IN ('Northeast', 'Southeast', 'Germany')
GROUP BY [SalesTerritoryRegion]
HAVING SUM(OrderQuantity) > 6000
ORDER BY Sales DESC
--ORDER BY SUM(SalesAmount) DESC

将 SUM() 与表达式一起使用

SQL Server SUM() 函数还允许您使用任何表达式或数学计算作为参数。例如,如果您有总订单和单位成本,您可以将它们相乘以获得实际销售额。在此基础上,使用 SUM() 函数计算根据条件产生的总收入。

假设我们的表中没有税值,但我们想计算包含税的总销售额。让我们假设税率为 8%。下面的查询计算了包含 8% 折扣的销售额,并将其添加到原始销售值中。接下来,SUM() 使用整个日期来计算总收入。

SELECT YEAR(OrderDate) Year, SUM(SalesAmount) AS Sales,
SUM(SalesAmount * 0.08) AS Tax,
SUM(SalesAmount + SalesAmount * 0.08) AS Total
FROM FactInternetSales
GROUP BY YEAR(OrderDate)
ORDER BY Year

除了通用的数学计算之外,SUM() 函数还允许您在其内部使用多个列。要组合这些列,您可以使用任何算术运算符在 SUM() 函数内部执行计算。

下面的查询将在 SUM() 函数中通过从销售额中减去产品成本来计算利润。

SELECT [SalesTerritoryCountry], SUM(TotalProductCost) AS Cost,
SUM(SalesAmount) AS Sales
,SUM(SalesAmount - TotalProductCost) AS Profit
FROM [DimSalesTerritory] st JOIN FactInternetSales fs
ON st.SalesTerritoryKey = fs.SalesTerritoryKey
GROUP BY [SalesTerritoryCountry]
SQL SUM Function With GROUP BY, HAVING, and WHERE clause

SQL SUM() 函数与子查询

无论您在上述示例中使用 WHERE 子句还是 HAVING 子句,我们都在尝试将表达式与一两个值进行比较。当您将 SUM() 函数和子查询结合使用时,您可以对常规数据和聚合数据执行更复杂的过滤。

下面的查询中的 SELECT 语句从 DimCustomer 表中选择 FirstName、LastName 和 YearlyIncome 列。WHERE 子句使用子查询查找所有客户的平均收入。接下来,它检查每条记录以查看客户的收入是否大于平均收入。如果为 True,则返回记录。

SELECT FirstName, LastName, YearlyIncome
FROM DimCustomer
WHERE YearlyIncome > (SELECT AVG(YearlyIncome) FROM DimCustomer)

类似地,您可以结合使用 SUM、SUBQUERY 和 HAVING 子句来检查销售额的聚合总和是否大于客户的平均销售额。

SELECT FirstName, LastName, SUM(SalesAmount) AS Sales
FROM DimCustomer JOIN FactInternetSales ON
DimCustomer.CustomerKey = FactInternetSales.CustomerKey
GROUP BY FirstName, LastName
HAVING SUM(SalesAmount) > (SELECT AVG(SalesAmount) FROM FactInternetSales)

使用 SUM() 和 EXISTS 操作符

EXISTS 操作符和 SUM() 函数的组合为执行最困难和最复杂的表搜索操作提供了更大的灵活性。下面的示例查询从事实表中返回所有出生年份低于 1980 年的客户的销售金额总和。

SELECT SUM(SalesAmount) [1980Sale] FROM FactInternetSales f
WHERE EXISTS (
SELECT 1 FROM DimCustomer c
WHERE c.CustomerKey = f.CustomerKey
AND YEAR(c.BirthDate) > 1980)

结果集是

1980Sale
---------------
4290192.0754

使用 SUM() 和 COUNT()

有些人可能会混淆 SUM() 和 COUNT() 函数,并认为它们是相同的。它们都是聚合函数,但它们完全不同且执行不同的操作。下面的列表显示了 SQL Server SUM() 和 COUNT() 函数之间的基本区别。

SUM()COUNT()
它查找数值的总和。它计算行的总数。
它忽略 NULL 值。如果您引用列名而不是 *,它会忽略 NULL。
SUM() 函数仅适用于数值列。COUNT() 适用于任何列并计算行数。
SELECT SUM(Sales) FROM productsSELECT COUNT(orderID) FROM products
用于查找总销售额、薪资总额等。用于计算总员工数、订单数、条目数等。

我举一个 SUM() 与 COUNT() 的例子以更好地理解。下面的查询中的 SUM() 函数将查找客户年收入的总和。接下来,COUNT() 函数返回客户总数。

SELECT SUM(YearlyIncome) AS Income, COUNT(CustomerKey) AS Customers
FROM DimCustomer
Income
----------
1059240000.00

类似地,您可以使用 GROUP BY 子句来查找属于男性和女性的客户总数和总和。

SELECT Gender, SUM(YearlyIncome) AS Income, COUNT(CustomerKey) AS Customers
FROM DimCustomer
GROUP BY Gender

结果是

性别收入客户
F523960000.009133
M535280000.009351

计算累积和(运行总计)

累积和就是运行总计,即从头到尾给定列中所有前一行值的总和。它是分析每个步骤的资金流动的非常有用的指标。

下面的查询使用 SQL SUM() 函数和 OVER 子句来计算列表价格按 Product Key 排序的运行总计或累积总和。这里,我们使用 IS NOT NULL 来从结果中删除 NULL 值。

SELECT [ProductKey],[EnglishProductName],[Color],[ListPrice]
,SUM(ListPrice) OVER(ORDER BY ProductKey) AS RunningTot
FROM [DimProduct]
WHERE ListPrice IS NOT NULL

如果您使用 PARTITION BY 子句,则运行总计将为每个分区重置其值。在这里,值将为每个产品颜色重置。

SELECT [ProductKey],[EnglishProductName],[Color],[ListPrice]
,SUM(ListPrice) OVER(PARTITION BY Color ORDER BY ProductKey) AS RunningTot
FROM [DimProduct]
WHERE ListPrice IS NOT NULL

如何计算滚动 SUM?

与运行总计不同,滚动 SUM 允许您指定用于计算的前续行数。它对于识别过去一周、10 天或 15 天的支出、销售额、订单等非常有用。

下面的 SQL SUM() 函数查询计算产品过去七天(包括当前行)的列表价格的滚动总和。这意味着当前行 + 前六天的成本。

SELECT [ProductKey],[EnglishProductName],[Color], [ListPrice], StartDate
,SUM(ListPrice) OVER(ORDER BY StartDate ROWS
BETWEEN 7 PRECEDING AND CURRENT ROW) AS RollingSum
FROM [DimProduct]
WHERE ListPrice IS NOT NULL
SQL SUM Function in Subquery, Rolling and Cumulative Total

SQL SUM() 函数处理 NULL 值

默认情况下,SUM() 函数会忽略 NULL 值,并查找给定列中总行的总和。因此,在使用 SUM() 函数时,您无需执行任何操作即可处理 NULL 值。但是,如果您想显式处理这些项,请使用 COALESCE 函数。

假设您有一个包含以下记录的产品表。

项目数量
笔记本电脑100
手机500
打印机空字符
台式机600

正如您所看到的,库存中没有打印机,并且它有一个 NULL 值。如果您使用 SUM() 函数计算总数量,它将返回 1200。

SELECT SUM(Quantity) AS Total

FROM Product

如果您想将 NULL 值替换为零,可以尝试 COALESCE 函数,如以下查询所示。

SELECT SUM(COALESCE(Quantity, 0)) AS Total

FROM Product

两个查询的结果将相同,但如果您的表包含许多记录并应用了分组,则所有 NULL 值都将显示为零。

提示:SUM() 函数按原样处理 NULL 值并返回 NULL 作为输出。要避免这种情况,请使用上述两个选项。