在没有 GROUP BY 的情况下使用 HAVING 无法按预期工作
Use of HAVING without GROUP BY not working as expected
我开始学习 SQL 服务器,在 msdn 中找到的文档中这样说
HAVING is typically used with a GROUP BY clause. When GROUP BY is not used, there is an implicit single, aggregated group.
这让我想到我们可以在没有 groupBy 子句的情况下使用 having,但是当我尝试进行查询时我无法使用它。
我有一个table这样的
CREATE TABLE [dbo].[_abc]
(
[wage] [int] NULL
) ON [PRIMARY]
GO
INSERT INTO [dbo].[_abc] (wage)
VALUES (4), (8), (15), (30), (50)
GO
现在当我 运行 这个查询时,我得到一个错误
select *
from [dbo].[_abc]
having sum(wage) > 5
错误:
文档正确;也就是说,你可以 运行 这个语句:
select sum(wage) sum_of_all_wages
, count(1) count_of_all_records
from [dbo].[_abc]
having sum(wage) > 5
您的语句不起作用的原因是 select *
,这意味着 select 每列的值。当没有group by
时,聚合所有记录;也就是说,您在结果集中只能得到 1 条记录,它必须代表每条记录。因此,您只能*包括通过将聚合函数应用于您的列而提供的值;不是列本身。
* 当然你也可以提供常量,这样select 'x' constant, count(1) cnt from myTable
就可以了。
我能想到的用例不多,你想在没有分组依据的情况下使用 having,但肯定可以如上所示完成。
注意:如果您想要工资大于 5 的所有行,则应改用 where
子句:
select *
from [dbo].[_abc]
where wage > 5
同样,如果你想要所有工资的总和大于5你可以这样做
select sum(wage) sum_of_wage_over_5
from [dbo].[_abc]
where wage > 5
或者,如果您想比较 5 岁以上和 5 岁以下的工资总和:
select case when wage > 5 then 1 else 0 end wage_over_five
, sum(wage) sum_of_wage
from [dbo].[_abc]
group by case when wage > 5 then 1 else 0 end
根据评论更新:
您需要 having
才能使用聚合函数吗?
没有。你可以 运行 select sum(wage) from [dbo].[_abc]
。当在没有 group by
子句的情况下使用聚合函数时,就像按常量分组一样;即 select sum(wage) from [dbo].[_abc] group by 1
.
文档仅表示虽然通常您会有一个带有 group by
语句的 having
语句,但可以排除 group by
/ 在这种情况下 having
语句,就像 select
语句一样,会将您的查询视为您指定了 group by 1
有什么意义?
很难想出很多好的用例,因为您只能返回一行,而 having
语句是对此的过滤器。
一个用例可能是您编写代码来监控某些软件的许可证;如果您的用户数少于每用户许可数,那么一切都很好/您不希望看到结果,因为您不在乎。如果您有更多用户,您想了解它。例如
declare @totalUserLicenses int = 100
select count(1) NumberOfActiveUsers
, @totalUserLicenses NumberOfLicenses
, count(1) - @totalUserLicenses NumberOfAdditionalLicensesToPurchase
from [dbo].[Users]
where enabled = 1
having count(1) > @totalUserLicenses
难道select与having从句无关吗?
是也不是。 Having 是对聚合数据的过滤器。 Select 说什么 columns/information 带回来。因此你必须问 "what would the result look like?" 即鉴于我们必须有效地应用 group by 1
来使用 having
语句,SQL 应该如何解释 select *
?由于您的 table 只有一列,因此这将转换为 select wage
;但是我们有 5 行,所以 wage
有 5 个不同的值,结果中只有 1 行显示这个。
我想你可以说 "I want to return all rows if their sum is greater than 5; otherwise I don't want to return any rows"。如果您的要求可以通过多种方式实现;其中之一是:
select *
from [dbo].[_abc]
where exists
(
select 1
from [dbo].[_abc]
having sum(wage) > 5
)
但是,我们必须编写满足要求的代码,而不是期望代码理解我们的意图。
考虑 having
的另一种方式是将 where
语句应用于子查询。 IE。您的原始声明实际上是:
select wage
from
(
select sum(wage) sum_of_wage
from [dbo].[_abc]
group by 1
) singleRowResult
where sum_of_wage > 5
那不会 运行 因为 wage
对外部查询不可用;仅返回 sum_of_wage
。
在 SQL 中,您不能 return 直接聚合函数列。您需要对非聚合字段进行分组
如下例
USE AdventureWorks2012 ;
GO
SELECT SalesOrderID, SUM(LineTotal) AS SubTotal
FROM Sales.SalesOrderDetail
GROUP BY SalesOrderID
HAVING SUM(LineTotal) > 100000.00
ORDER BY SalesOrderID ;
在你的情况下,你的 table 没有身份列,它应该如下所示
Alter _abc
Add Id_new Int Identity(1, 1)
Go
没有 GROUP BY
子句的 HAVING
是完全有效的,但您需要了解以下内容:
- 结果将包含零行或一行
- 即使
WHERE
条件匹配零行 ,隐式 GROUP BY
也将 return 恰好一行
HAVING
将根据条件 保留或删除该单行
SELECT
子句中的任何列都需要包含在聚合函数中
- 您也可以指定一个表达式,只要它在功能上不依赖于列
这意味着你可以这样做:
SELECT SUM(wage)
FROM employees
HAVING SUM(wage) > 100
-- One row containing the sum if the sum is greater than 5
-- Zero rows otherwise
甚至这样:
SELECT 1
FROM employees
HAVING SUM(wage) > 100
-- One row containing "1" if the sum is greater than 5
-- Zero rows otherwise
当您有兴趣检查是否找到聚合匹配项时,通常会使用此构造:
SELECT *
FROM departments
WHERE EXISTS (
SELECT 1
FROM employees
WHERE employees.department = departments.department
HAVING SUM(wage) > 100
)
-- all departments whose employees earn more than 100 in total
我开始学习 SQL 服务器,在 msdn 中找到的文档中这样说
HAVING is typically used with a GROUP BY clause. When GROUP BY is not used, there is an implicit single, aggregated group.
这让我想到我们可以在没有 groupBy 子句的情况下使用 having,但是当我尝试进行查询时我无法使用它。
我有一个table这样的
CREATE TABLE [dbo].[_abc]
(
[wage] [int] NULL
) ON [PRIMARY]
GO
INSERT INTO [dbo].[_abc] (wage)
VALUES (4), (8), (15), (30), (50)
GO
现在当我 运行 这个查询时,我得到一个错误
select *
from [dbo].[_abc]
having sum(wage) > 5
错误:
文档正确;也就是说,你可以 运行 这个语句:
select sum(wage) sum_of_all_wages
, count(1) count_of_all_records
from [dbo].[_abc]
having sum(wage) > 5
您的语句不起作用的原因是 select *
,这意味着 select 每列的值。当没有group by
时,聚合所有记录;也就是说,您在结果集中只能得到 1 条记录,它必须代表每条记录。因此,您只能*包括通过将聚合函数应用于您的列而提供的值;不是列本身。
* 当然你也可以提供常量,这样select 'x' constant, count(1) cnt from myTable
就可以了。
我能想到的用例不多,你想在没有分组依据的情况下使用 having,但肯定可以如上所示完成。
注意:如果您想要工资大于 5 的所有行,则应改用 where
子句:
select *
from [dbo].[_abc]
where wage > 5
同样,如果你想要所有工资的总和大于5你可以这样做
select sum(wage) sum_of_wage_over_5
from [dbo].[_abc]
where wage > 5
或者,如果您想比较 5 岁以上和 5 岁以下的工资总和:
select case when wage > 5 then 1 else 0 end wage_over_five
, sum(wage) sum_of_wage
from [dbo].[_abc]
group by case when wage > 5 then 1 else 0 end
根据评论更新:
您需要 having
才能使用聚合函数吗?
没有。你可以 运行 select sum(wage) from [dbo].[_abc]
。当在没有 group by
子句的情况下使用聚合函数时,就像按常量分组一样;即 select sum(wage) from [dbo].[_abc] group by 1
.
文档仅表示虽然通常您会有一个带有 group by
语句的 having
语句,但可以排除 group by
/ 在这种情况下 having
语句,就像 select
语句一样,会将您的查询视为您指定了 group by 1
有什么意义?
很难想出很多好的用例,因为您只能返回一行,而 having
语句是对此的过滤器。
一个用例可能是您编写代码来监控某些软件的许可证;如果您的用户数少于每用户许可数,那么一切都很好/您不希望看到结果,因为您不在乎。如果您有更多用户,您想了解它。例如
declare @totalUserLicenses int = 100
select count(1) NumberOfActiveUsers
, @totalUserLicenses NumberOfLicenses
, count(1) - @totalUserLicenses NumberOfAdditionalLicensesToPurchase
from [dbo].[Users]
where enabled = 1
having count(1) > @totalUserLicenses
难道select与having从句无关吗?
是也不是。 Having 是对聚合数据的过滤器。 Select 说什么 columns/information 带回来。因此你必须问 "what would the result look like?" 即鉴于我们必须有效地应用 group by 1
来使用 having
语句,SQL 应该如何解释 select *
?由于您的 table 只有一列,因此这将转换为 select wage
;但是我们有 5 行,所以 wage
有 5 个不同的值,结果中只有 1 行显示这个。
我想你可以说 "I want to return all rows if their sum is greater than 5; otherwise I don't want to return any rows"。如果您的要求可以通过多种方式实现;其中之一是:
select *
from [dbo].[_abc]
where exists
(
select 1
from [dbo].[_abc]
having sum(wage) > 5
)
但是,我们必须编写满足要求的代码,而不是期望代码理解我们的意图。
考虑 having
的另一种方式是将 where
语句应用于子查询。 IE。您的原始声明实际上是:
select wage
from
(
select sum(wage) sum_of_wage
from [dbo].[_abc]
group by 1
) singleRowResult
where sum_of_wage > 5
那不会 运行 因为 wage
对外部查询不可用;仅返回 sum_of_wage
。
在 SQL 中,您不能 return 直接聚合函数列。您需要对非聚合字段进行分组
如下例
USE AdventureWorks2012 ;
GO
SELECT SalesOrderID, SUM(LineTotal) AS SubTotal
FROM Sales.SalesOrderDetail
GROUP BY SalesOrderID
HAVING SUM(LineTotal) > 100000.00
ORDER BY SalesOrderID ;
在你的情况下,你的 table 没有身份列,它应该如下所示
Alter _abc
Add Id_new Int Identity(1, 1)
Go
GROUP BY
子句的 HAVING
是完全有效的,但您需要了解以下内容:
- 结果将包含零行或一行
- 即使
WHERE
条件匹配零行 ,隐式 HAVING
将根据条件 保留或删除该单行
GROUP BY
也将 return 恰好一行 - 即使
SELECT
子句中的任何列都需要包含在聚合函数中- 您也可以指定一个表达式,只要它在功能上不依赖于列
这意味着你可以这样做:
SELECT SUM(wage)
FROM employees
HAVING SUM(wage) > 100
-- One row containing the sum if the sum is greater than 5
-- Zero rows otherwise
甚至这样:
SELECT 1
FROM employees
HAVING SUM(wage) > 100
-- One row containing "1" if the sum is greater than 5
-- Zero rows otherwise
当您有兴趣检查是否找到聚合匹配项时,通常会使用此构造:
SELECT *
FROM departments
WHERE EXISTS (
SELECT 1
FROM employees
WHERE employees.department = departments.department
HAVING SUM(wage) > 100
)
-- all departments whose employees earn more than 100 in total