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)
错误:
文档正确;即你可以运行这个语句:
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)
是和不是。拥有是对聚合数据的过滤器。选择表示要带回哪些列/信息。因此,你必须问“结果会是什么样子?” 即考虑到我们必须有效地应用group by 1该having语句,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返回。
| 归档时间: |
|
| 查看次数: |
5606 次 |
| 最近记录: |