SQL HAVING 子句

SQL Server HAVING 子句与 WHERE 子句非常相似,因为两者都有助于根据给定的表达式筛选表记录。正如我们已经知道的,WHERE 子句不允许我们针对聚合数据检查任何条件。因此,为了筛选分组数据或针对聚合数据检查条件,我们必须使用 HAVING 子句。

定义是这样的:SQL HAVING 子句用于限制 GROUP BY 语句返回的行(或记录)的数量。这意味着要使用 HAVING 子句,我们必须使用 GROUP BY,因为 HAVING 会筛选我们从该集合中获取的数据。如果我们没有使用分组,那么 HAVING 子句的行为就像 WHERE,并且在实时环境中会抛出错误。

此外,WHERE 条件在实施 服务器 GROUP BY 之前应用于每一行。但是,SQL Server HAVING 子句将在分组发生之后应用。通常,HAVING 子句可以与聚合函数(如 AVG()、COUNT()、MIN()、MAX()、SUM() 等)一起使用。

例如,如果要按部门检索员工工资,可以按部门对员工进行分组。接下来,使用聚合 SUM() 函数查找按部门分组的工资。但是,在某些情况下,您需要几个部门的工资,或者找出哪个部门的工资最高。在这种情况下,您可以使用 SQL HAVING 子句来筛选聚合结果:HAVING SUM(Salaries) > 1000000。本文将展示如何在 GROUP BY 应用聚合函数后编写 HAVING 子句来筛选数据。

SQL HAVING 子句语法

HAVING 子句与 GROUP BY 语句一起工作,其语法如下所示

SELECT [Column1], [Column2]...[ColumnN],
Aggregate Function (Expression | Column_Name)
FROM [Source]
WHERE [Conditions] -- Optional
GROUP BY [Column1], [Column2]...[ColumnN]
HAVING [Conditions] -- This Condition is on Aggregate Function (Expression | Column_Name)
ORDER BY ASC|DESC

在上述语法中,SQL Server HAVING 子句支持的聚合函数包括 SUM()、COUNT()、MIN()、MAX()、AVG() 等。

  • [Column1],…[ColumnN]: 您想从数据库表中检索的列名列表。请记住,这些列名必须包含在 GROUP BY 中。
  • 聚合函数:这里,您必须使用任何可用的聚合函数来查找计数、最小值、最大值、总和等。
  • WHERE 条件:它是可选的,如果您想在分组之前筛选行,请使用它。
  • GROUP BY:请在此处包含所有非聚合列。否则,它将抛出错误。
  • HAVING 条件:在这里,您可以使用聚合函数来筛选分组的行。
  • ORDER BY:它是可选的,有助于按升序或降序对结果进行排序。

我们已经解释了聚合函数、条件和 Group By 选项。因此,在查看此内容之前,请参阅Group By。在这里,SQL Server HAVING 子句有助于在从 Group By 获取的聚合数据上提供筛选器或条件。

让我给您一些 HAVING 子句的简单和复杂的示例,以便您了解其工作原理。

SQL HAVING 子句单条件示例

让我向您展示如何编写带有单个条件的 HAVING 子句。为此,我们使用 Adventure Works 数据库表。在此示例中,我们对多个列使用 MAX 和 SUM 函数,并在 HAVING 子句中使用 SUM() 函数来筛选 SELECT 语句数据。

SELECT Color, SUM(SalesAmount) AS [Total Sales], 
MAX(SalesAmount) AS [Maximum Sale]
FROM DimProduct pr
JOIN FactInternetSales fact ON fact.ProductKey = pr.ProductKey
GROUP BY Color
HAVING SUM(SalesAmount) > 150000

在上述查询中,GROUP BY 语句将按颜色对产品进行分组。接下来,SQL HAVING 子句将检查条件,即组的总销售额或销售额总和是否大于 150000。SELECT 语句选择产品颜色、最大销售额和销售额总和。

将 HAVING 子句与多个条件一起使用

此示例将 SQL Server HAVING 子句与使用 AND 和 NOT 运算符组合的多个条件一起使用。首先,查询将按颜色组织 Dim Products。接下来,HAVING 子句将检查条件,即 SUM([SalesAmount]) 是否大于 10000 并且订单数量的总和不小于 3500。

SELECT Color,
SUM(SalesAmount) AS [Total Sales],
SUM([OrderQuantity]) as [Total Orders]
FROM DimProduct pr
JOIN FactInternetSales fact ON fact.ProductKey = pr.ProductKey
GROUP BY Color
HAVING SUM(SalesAmount) > 10000 AND NOT SUM(OrderQuantity) < 3500

SQL Server HAVING 子句与 SUM() 函数

如前所述,您可以在 HAVING 子句中使用任何可用的聚合函数来筛选分组记录。在本系列示例中,我们首先从带有简单单个条件的 SUM() 函数 HAVING 子句开始。

在下面的查询中,首先,Group By 将按其英文职业对 Dim 客户进行分类。接下来,SUM() 函数将找到每个职业组中所有记录的总和。第三步是执行 HAVING 子句。在这里,它将检查条件,即 [YearlyIncome] 的总和是否大于 200000000。

SELECT [EnglishOccupation]
,SUM([YearlyIncome]) AS [Total Income]
FROM [DimCustomer]
GROUP BY [EnglishOccupation]
HAVING SUM([YearlyIncome]) > 200000000
SQL HAVING clause syntax with GROUP BY example

这是另一个示例,它将查找每个客户职业组的销售总额。然后,它打印总销售额大于 3000000 的客户。

SELECT [EnglishOccupation]
,SUM(SalesAmount) AS [Total Sales]
FROM [DimCustomer] JOIN FactInternetSales
ON DimCustomer.CustomerKey = FactInternetSales.CustomerKey
GROUP BY [EnglishOccupation]
HAVING SUM(SalesAmount) > 3000000

按 COUNT 函数筛选组

此示例在 HAVING 子句中使用 SQL COUNT 函数来查找或计算每个产品颜色中的产品总数。接下来,它显示产品计数至少为 10 的颜色组。

SELECT Color, COUNT([ProductKey]) AS TotalProducts
FROM [DimProduct]
GROUP BY Color
HAVING COUNT([ProductKey]) > 10

SQL HAVING 子句与 MAX() 函数

MAX 函数和 HAVING 子句的组合将找到每个组中的最大值。在此基础上,您可以根据此最大值进一步筛选组。

例如,下面的查询将根据客户的职业对 DimCustomer 进行分组。然后,它返回每个职业中的最高年收入。HAVING 子句将筛选这些 EnglishOccupation 组,并返回最高收入大于 10000 的客户。

SELECT [EnglishOccupation]
,MAX([YearlyIncome]) AS [Highest Income]
FROM [DimCustomer]
GROUP BY [EnglishOccupation]
HAVING MAX([YearlyIncome]) > 10000

让我向您展示 SQL HAVING 子句与 MAX() 和 SUM() 函数的另一个示例。SELECT 语句选择职业、教育、年收入总和和最大销售额。接下来,它按客户的英文职业和教育对 Sim 客户进行分组。HAVING 子句将检查条件,即 MAX([YearlyIncome]) > 80000。

SELECT [EnglishOccupation], EnglishEducation
,SUM([YearlyIncome]) AS [Total Income]
,MAX([YearlyIncome]) AS [Highest Income]
FROM [DimCustomer]
GROUP BY [EnglishOccupation], EnglishEducation
HAVING MAX([YearlyIncome]) > 80000
ORDER BY EnglishOccupation

使用 HAVING 与 MIN 函数示例

您还可以将聚合 MIN 函数与 SQL HAVING 子句一起使用,以对选定数据应用最小或最低筛选。下面的查询将按职业对客户进行分组,并显示相应职业中的最低收入。接下来,HAVING 子句将通过显示年收入大于 10000 的 EnglishOccupation 组来筛选这些职业组。

SELECT [EnglishOccupation]
,MIN([YearlyIncome]) AS [Lowest Income]
FROM [DimCustomer]
GROUP BY [EnglishOccupation]
HAVING MIN([YearlyIncome]) > 10000

SQL HAVING 子句与 AVG() 函数

此示例使用 AVG 聚合函数来查找每个客户职业组的平均收入。接下来,带有 AVG() 组合的 HAVING 子句将显示平均职业大于 40000 的组。

SELECT [EnglishOccupation]
,AVG([YearlyIncome]) AS [Average Income]
FROM [DimCustomer]
GROUP BY [EnglishOccupation]
HAVING AVG([YearlyIncome]) > 40000

如果您熟悉联接,可以尝试以下代码。此查询将查找每个客户职业的平均销售额。接下来,它打印平均销售额大于 450 的组。

SELECT [EnglishOccupation]
,AVG(SalesAmount) AS [Average Sales]
FROM [DimCustomer] JOIN FactInternetSales
ON DimCustomer.CustomerKey = FactInternetSales.CustomerKey
GROUP BY [EnglishOccupation]
HAVING AVG(SalesAmount) > 450
HAVING clause query to filter aggregated data

SQL HAVING 与 ORDER BY 子句

正如您已经知道的,ORDER BY 子句有助于按升序或降序对选定数据进行排序。您可以将Order By 与 HAVING 子句一起使用,以按 ASC 或 DESC 对筛选后的分组数据进行排序。

在此示例中,我们使用 MAX()、MIN() 和 SUM() 聚合函数来查找客户职业组的最大、最小和总收入。接下来,HAVING 子句将筛选最大收入大于 30000 阈值的客户组。接下来,ORDER BY 子句将按总年收入降序对整个结果集表进行排序。

SELECT [EnglishOccupation]
,SUM([YearlyIncome]) AS [Total Income]
,MAX([YearlyIncome]) AS [Maximum Income]
,MIN([YearlyIncome]) AS [Minimum Income]
FROM [DimCustomer]
GROUP BY [EnglishOccupation]
HAVING MAX([YearlyIncome]) > 30000
--ORDER BY EnglishOccupation
ORDER BY [Total Income] DESC

逻辑 AND、OR、NOT 运算符筛选数据

正如我们已经在上面的示例中提到的,SQL HAVING 子句可以与单个条件或多个条件一起使用,具体取决于要求。在使用多个条件时,您可以使用任何现有的逻辑运算符,包括 AND、OR 和 NOT。

  • AND:当两个条件都返回 TRUE 时,它返回记录。
  • OR:如果满足两个条件中的任何一个,查询将返回行。
  • NOT:它返回相反的结果。

以下查询使用 DimProduct 和 FactInternetSales 表来显示 HAVING 子句中的逻辑运算符。首先,查询按颜色对产品进行分组,并计算每种颜色的产品总数以及各自的销售额。在 SQL HAVING 子句中,我们使用了两个条件,并使用逻辑 AND 运算符组合,以便两个条件都必须满足才能显示该特定产品颜色组。为了显示记录,销售额总和必须大于 10000,并且产品数量应小于 9000。

SELECT Color, COUNT(pr.[ProductKey]) AS TotalProducts,
SUM(SalesAmount) AS [Total Sales]
FROM DimProduct pr
JOIN FactInternetSales fact ON fact.ProductKey = pr.ProductKey
GROUP BY Color
HAVING SUM(SalesAmount) > 10000 AND COUNT(pr.[ProductKey]) < 9000
HAVING clause with Multiple Conditions

SQL Server Having 与 Where

尽管 WHERE 和 HAVING 子句都用于筛选选定数据,但它们的方法却大相径庭。例如,WHERE 条件将在 GROUP BY 子句对单个行进行分组之前应用于它们。接下来,SQL HAVING 子句将对这些分组的行应用筛选器。下表列出了 HAVING 和 WHERE 子句之间的一些差异。

HAVINGWHERE
它在 GROUP BY 之后执行。它在创建组之后使用,因此它是后置筛选器。WHERE 子句在 GROUP BY 或创建组之前执行,因此它是前置筛选器。
SQL HAVING 子句使用给定条件来筛选聚合或分组数据。因此,它不是检查单个行,而是检查分组行的条件。WHERE 子句使用给定条件来筛选 SELECT 语句中的每一行。因此,它检查单个表行的条件,而不是分组行的条件。
HAVING 需要 GROUP BY 子句来组合或分组相同类型的记录。由于对输出没有影响,WHERE 子句可以与 GROUP BY 一起或不一起执行。
查询的执行顺序是先 GROUP BY,然后是 HAVING 子句。查询的执行顺序是先使用 WHERE 子句,然后是 GROUP BY。
它用于使用聚合函数筛选数据。您不能在 WHERE 子句中使用聚合函数来筛选数据。

我们还可以将 WHERE 条件与此子句一起使用。下一节将介绍相同的内容。

结合 WHERE 和 HAVING 子句

在上一节中,我们展示了 HAVING 和 WHERE 子句之间的区别,但我们可以结合使用两者来应用多个筛选器。例如,您需要一个 WHERE 子句来对单个行应用特定条件,以从分组中排除一些行。一旦分组发生,HAVING 子句将应用额外的筛选器来限制分组的行。

此示例展示了 SQL Server Having 与 Where 子句。以下查询将首先提取教育不等于 [Partial High School] 的客户数据。

接下来,GROUP BY 语句将按职业和教育对客户进行分组。最后,HAVING 子句将检查条件,即 MAX([YearlyIncome]) 是否大于 90000。因此,在分组发生之前,表数据会进行筛选:教育不是“Partial High School”的客户。

SELECT [EnglishOccupation], EnglishEducation
,SUM([YearlyIncome]) AS [Total Income]
,MAX([YearlyIncome]) AS [Highest Income]
FROM [DimCustomer]
WHERE EnglishEducation <> 'Partial High School'
GROUP BY [EnglishOccupation], EnglishEducation
HAVING MAX([YearlyIncome]) > 90000
ORDER BY EnglishOccupation

为了解释差异,我们注释掉了查询的最后一行。请检查左下方图像中教育不是 Partial High School 的所有记录。

这次,我们注释掉了 WHERE 条件。这就是为什么您在右下方图像的结果窗口中看到 Partial High School 的原因。

Difference between SQL HAVING and WHERE clause

SQL HAVING 子句中的嵌套子查询

以下查询是一个简单的示例,用于演示 HAVING 子句中的嵌套子查询。

  • 主 SELECT 语句将按产品名称对产品进行分组,并计算每个产品名称的销售额总和。
  • HAVING 子句有一个 SUM(fact.SalesAmount) 条件,用于检查每个产品名称组的销售额是否大于子查询(平均)值。
  • 子查询有一个嵌套查询,其中内部查询查找每个产品键组的销售额总和。接下来,子查询查找它们的平均值。
  • 最终结果将是总销售额大于平均销售额的产品。
SELECT pr.EnglishProductName, SUM(fact.SalesAmount) AS [Total Sales]
FROM FactResellerSales fact
JOIN DimProduct pr ON fact.ProductKey = pr.ProductKey
GROUP BY pr.EnglishProductName
HAVING SUM(fact.SalesAmount) > (SELECT AVG(Sales) FROM
(SELECT SUM(ft.SalesAmount) AS Sales
FROM FactResellerSales ft GROUP BY ft.ProductKey) AS AvgSales);

它生成 88 行结果,我们只显示前 6 条记录

英文产品名称总销售额
HL 山地车架 – 黑色,38501788.1977
HL 山地车架 – 黑色,42901590.2336
HL 山地车架 – 银色,38930780.6807
HL 山地车架 – 银色,42269874.0096
HL 山地车架 – 银色,46499556.5724
HL 公路车架 – 黑色,44375026.9772

如果您对 HAVING 子句中的多级嵌套子查询感到困惑,以下示例是一个非常简单的示例。在这里,SQL HAVING 子句使用一个子查询,它查找整个 FactInternetSales 表的平均销售额。

SELECT pr.EnglishProductName, SUM(fact.SalesAmount) AS [Total Sales]
FROM FactResellerSales fact
JOIN DimProduct pr ON fact.ProductKey = pr.ProductKey
GROUP BY pr.EnglishProductName
HAVING SUM(fact.SalesAmount) > (SELECT AVG(SalesAmount) FROM FactResellerSales)

它生成 244 行结果,我们显示结果集的前 5 行

英文产品名称总销售额
AWC 标志帽31541.3461
洗车液 – 溶解剂11188.3725
缆锁16225.22
链条9377.7102
经典背心,M77614.101

SQL HAVING 子句常见错误和最佳实践

以下是您可能遇到的一些常见错误列表以及避免这些错误的最佳实践。

  1. 当您使用 HAVING 子句时,您在 SELECT 语句中选择的列应放置在 GROUP BY 子句中或聚合函数中。否则,查询将抛出错误。
  2. 始终建议为聚合结果使用 ALIAS 列名。这样结果应该更有意义和易于理解。
  3. SQL HAVING 子句只对分组记录(而不是单个行)起作用,因此您必须将其与 GROUP BY 子句结合使用。
  4. 了解 HAVING 子句查询执行的顺序很重要;否则,您可能会混淆结果集。执行顺序是:SELECT -> FROM -> WHERE -> GROUP BY -> HAVING -> -> ORDER BY。
  5. 处理大型数据集时,在应用聚合和分组之前使用 WHERE 条件筛选数据,以避免加载。
  6. 要执行数字比较,您可以使用可用的比较运算符列表,例如 =、!=、>、< 等。
  7. 使用逻辑运算符(AND、NOT 和 OR)在 HAVING 子句中组合多个聚合函数。
  8. 在 WHERE 条件中使用聚合函数筛选数据将抛出错误,因此请使用 HAVING 子句。

评论已关闭。