ROLLUP允许在多个分组级别进行聚合,就像我UNIONed多个简单的SELECT语句一样。
但我希望能够聚合较低级别的分组结果,就像我使用嵌套的SELECT语句或依赖于彼此的CTE链一样。
例如,我希望能够从较低级别的分组中计算组数或计算较低级别或最小值的较低级别的总和的平均值,e.t.c。
更具体的例子:如果我有美国每次车祸的记录,我不仅要知道ROLLUP(州,县,市,邮编)各个级别的事故,还要计算人数(显然每个人可能涉及多起事故,因而涉及多个记录。)
用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'.