ROLLUP 允许跨多个级别的分组进行聚合,就好像我联合了多个简单的 SELECT 语句一样。
但我希望能够聚合较低级别分组的结果,就像我使用嵌套的 SELECT 语句或相互依赖的 CTE 链一样。
例如,我希望能够从较低级别的分组中计算组数或计算较低级别的总和的平均值或较低级别的最大值的最小值等
更具体的例子:如果我有美国每起车祸的记录,我不仅想得到 ROLLUP(state,county,city,zip) 中每个级别的事故计数,还想得到人数(显然每个人可能是涉及多起事故并因此产生多份记录)。
是否可以使用 ROLLUP 来实现?如果可能,那么如何?
带有结果的 SQL 示例:
if object_id('accident') is not null drop table accident
create table accident(
id int identity(1,1)
,state varchar(50)
,city varchar(50)
,zip varchar(50)
,person varchar(50)
)
insert accident(state,city,zip,person)values
('NY','Manhattan',10001,'John')
,('NY','Manhattan',10001,'John')
,('NY','Manhattan',10001,'Barbara')
select
state,city,zip,person
,accidents=count(1)
-- the following line causes error: Windowed functions cannot be used in the context of another windowed function or aggregate.
--,people=sum(case when row_number()over(partition by person order by (select 0))=1 then 1 else 0 end)
from accident
group by rollup(state,city,zip,person)
;with person as (select state,city,zip,person from accident group by state,city,zip,person)
select
state,city,zip
,people=count(1)
from person
group by rollup(state,city,zip)
结果:
state city zip person accidents
NY Manhattan 10001 Barbara 1
NY Manhattan 10001 John 2
NY Manhattan 10001 NULL 3
NY Manhattan NULL NULL 3
NY NULL NULL NULL 3
NULL NULL NULL NULL 3
state city zip people
NY Manhattan 10001 2
NY Manhattan NULL 2
NY NULL NULL 2
NULL NULL NULL 2
查看第一个结果为每个级别返回 3 个事故,第二个返回 2。如果想在一个 ROLLUP 查询中同时获得 3 和 2。我的问题是窗口函数不能嵌套。
我刚才问的可以通过这个查询来实现:
;with person as (select state,city,zip,person,accidents=count(1) from accident group by state,city,zip,person)
select
state,city,zip
,accidents=sum(accidents)
,people=count(1)
from person
group by rollup(state,city,zip)
state city zip accidents people
NY Manhattan 10001 3 2
NY Manhattan NULL 3 2
NY NULL NULL 3 2
NULL NULL NULL 3 2
但是这样做需要明确地为每个级别编写 CTE。
我希望能够编写一个可以访问较低级别分组结果的查询,而不管分组级别的数量如何。
试过这个:
;with
lvl as (
select *
,lvl = -1
,accidents=1
,people=1
from accident
union all
select accident.*
,lvl = grouping_id(accident.state,accident.city,accident.zip,accident.person)
,accidents=sum(accidents)
,people=count(1)
from accident
join lvl prev on prev.lvl = (grouping_id(accident.state,accident.city,accident.zip,accident.person)+1)/2-1
group by rollup(accident.state,accident.city,accident.zip,accident.person)
)
select * from lvl
但出现错误:
Msg 1015, Level 15, State 1, ...
An aggregate cannot appear in an ON clause unless it is in a subquery contained in a HAVING clause or select list, and the column being aggregated is an outer reference.
Msg 467, Level 16, State 1, ...
GROUP BY, HAVING, or aggregate functions are not allowed in the recursive part of a recursive common table expression 'lvl'.
相关问题:具有汇总逻辑的递归 sql 函数?