给定一个看起来像这样的成员表:
create table dbo.members
(
member_number int not null primary key ,
start_date datetime not null ,
end_date datetime not null ,
)
还有一个生成连续整数序列的表值函数,如下所示:
create function dbo.IntegerRange ( @from int , @thru int )
returns @sequence table
(
value int not null primary key clustered
)
as
begin
declare @increment int = case when @from > @thru then -1 else 1 end ;
with sequence(value) as
(
select value = @from
union all
select value + @increment
from sequence
where value < @thru
)
insert @sequence
select value
from sequence
order by value
option ( MAXRECURSION 0 )
return
end
像这样的查询应该给你你想要的:
select [year] = period.yyyy ,
[month] = case period.mm ,
when 1 then 'Jan'
when 2 then 'Feb'
when 3 then 'Mar'
when 4 then 'Apr'
when 5 then 'May'
when 6 then 'Jun'
when 7 then 'Jul'
when 8 then 'Aug'
when 9 then 'Sep'
when 10 then 'Oct'
when 11 then 'Nov'
when 12 then 'Dev'
else '***'
end ,
member_cnt = sum( case when m.member_number is not null then 1 else 0 end )
from ( select yyyy = yyyy.value ,
mm = mm.value ,
dtFrom = dateadd( month , mm.value - 1 , dateadd( year , yyyy.value - 1900 , convert(date,'') ) ) ,
dtThru = dateadd( day , - 1 , dateadd( month , mm.value , dateadd( year , yyyy.value - 1900 , convert(date,'') ) ) )
from dbo.IntegerRange(2000,2013) yyyy
full join dbo.IntegerRange(1,12) mm on 1=1
) period
left join dbo.members m on period.dtFrom <= m.end_date
and period.dtThru >= m.start_date
group by period.yyyy ,
period.mm
order by period.yyyy ,
period.mm
子句中的第一个表表达式from
创建了一个涵盖报告期间的期间(在本例中为几个月,但该技术不将自身限制为几个月甚至几周)的虚拟表:
from ( select yyyy = yyyy.value ,
mm = mm.value ,
dtFrom = dateadd( month , mm.value - 1 , dateadd( year , yyyy.value - 1900 , convert(date,'') ) ) ,
dtThru = dateadd( day , - 1 , dateadd( month , mm.value , dateadd( year , yyyy.value - 1900 , convert(date,'') ) ) )
from dbo.IntegerRange(2000,2013) yyyy
full join dbo.IntegerRange(1,12) mm on 1=1
) period
然后通过 a 将其连接起来left outer join
,确保将所有期间(而不仅仅是那些具有活跃成员的期间)报告到成员表中,以针对上述虚拟period
表中的每个报告期收集在该期间活跃的成员集:
left join dbo.members m on period.dtFrom <= m.end_date
and period.dtThru >= m.start_date
然后我们按每个时期的年和月分组,然后按年/月数对结果进行排序:
group by period.yyyy ,
period.mm
order by period.yyyy ,
period.mm
在创建要返回的结果集时,我们返回期间的年份、月份数(转换为友好名称)和活动成员的数量。请注意,我们必须在sum()
此处使用聚合函数,而不是count()
因为空句点将返回单行(null
在所有列中)。Count()
与所有其他聚合函数不同,它null
在聚合中包含值。Sum()
应用于作为判别函数的 case 表达式,返回 1 或 0,标识该行是表示有用数据还是缺失数据:
select [year] = period.yyyy ,
[month] = case period.mm ,
when 1 then 'Jan'
when 2 then 'Feb'
when 3 then 'Mar'
when 4 then 'Apr'
when 5 then 'May'
when 6 then 'Jun'
when 7 then 'Jul'
when 8 then 'Aug'
when 9 then 'Sep'
when 10 then 'Oct'
when 11 then 'Nov'
when 12 then 'Dev'
else '***'
end ,
member_cnt = sum( case when m.member_number is not null then 1 else 0 end )
简单的!