在没有 GROUP BY 的情况下使用 HAVING 无法按预期工作

Lij*_*raj 1 sql sql-server group-by having

我开始学习SQL Server,在msdn中找到的文档是这样的

HAVING 通常与 GROUP BY 子句一起使用。当不使用 GROUP BY 时,存在一个隐式的单个聚合组。

这让我认为我们可以在没有 groupBy 子句的情况下使用having,但是当我尝试进行查询时我无法使用它。

我有一张这样的桌子

CREATE TABLE [dbo].[_abc]
(
    [wage] [int] NULL
) ON [PRIMARY]
GO

INSERT INTO [dbo].[_abc] (wage)
VALUES (4), (8), (15), (30), (50) 
GO
Run Code Online (Sandbox Code Playgroud)

现在,当我运行此查询时,出现错误

select * 
from [dbo].[_abc]
having sum(wage) > 5
Run Code Online (Sandbox Code Playgroud)

错误:

在此输入图像描述

Joh*_*van 5

文档正确;即你可以运行这个语句:

select sum(wage) sum_of_all_wages
, count(1) count_of_all_records
from [dbo].[_abc] 
having sum(wage) > 5
Run Code Online (Sandbox Code Playgroud)

您的语句不起作用的原因是因为select *,这意味着选择每个列的值。当没有 时group by,所有记录被聚合;即,结果集中只有 1 条记录,它必须代表每条记录。因此,您只能*包含通过将聚合函数应用于列而提供的值;不是列本身。* 当然,你也可以提供常量,这样select 'x' constant, count(1) cnt from myTable就可以了。

我能想到的用例并不多,您想在没有 group by 的情况下使用having,但当然可以如上所示完成。

注意:如果您想要工资大于 5 的所有行,您可以使用该where子句:

select * 
from [dbo].[_abc] 
where wage > 5
Run Code Online (Sandbox Code Playgroud)

同样,如果您希望所有工资的总和大于 5,您可以这样做

select sum(wage) sum_of_wage_over_5 
from [dbo].[_abc] 
where wage > 5
Run Code Online (Sandbox Code Playgroud)

或者,如果您想将超过 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 
Run Code Online (Sandbox Code Playgroud)

请参阅此处的可运行示例


根据评论更新:

需要having使用聚合函数吗?

不,你可以跑select sum(wage) from [dbo].[_abc]。当不带子句使用聚合函数时group by,就好像按常量进行分组一样;IE select sum(wage) from [dbo].[_abc] group by 1

该文档仅意味着,虽然通常您会有一个having带有语句的语句,但在这种情况下group by排除 / 是可以的,该语句与该语句一样,会将您的查询视为您指定的group byhavingselectgroup 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 
Run Code Online (Sandbox Code Playgroud)

select 不是与having 子句无关吗?

是和不是。拥有是对聚合数据的过滤器。选择表示要带回哪些列/信息。因此,你必须问“结果会是什么样子?” 即考虑到我们必须有效地应用group by 1having语句,SQL 应该如何解释select *?由于您的表只有一列,这将转换为select wage;但我们有 5 行,因此 5 个不同的值wage,并且结果中只有 1 行来显示这一点。

我想您可能会说“如果总和大于 5,我想返回所有行;否则我不想返回任何行”。如果您有这样的要求,可以通过多种方式实现;其中之一是:

select *
from [dbo].[_abc] 
where exists 
(
    select 1 
    from [dbo].[_abc] 
    having sum(wage) > 5
) 
Run Code Online (Sandbox Code Playgroud)

然而,我们必须编写代码来满足需求,而不是期望代码能够理解我们的意图。

另一种思考方式having是作为where应用于子查询的语句。即,您的原始声明实际上是这样的:

select wage
from
(
    select sum(wage) sum_of_wage
    from [dbo].[_abc]
    group by 1
) singleRowResult
where sum_of_wage > 5
Run Code Online (Sandbox Code Playgroud)

这不会运行,因为wage外部查询不可用;仅sum_of_wage返回。