我有一个包含以下列的成员资格表:
Member_number | StartDate | EndDate
XYZ | 01-Jan-2002 | 01-March-2002
ABC | 01-Feb-2002 | 01-March-2002
基本上,我想说明特定月份有多少成员出席。我的问题是我不知道如何将这段时间打破几个月。我怎么能看到这个结果?
Month | NumberOfMembers
Jan | 1
Feb | 2
March | 2
答案 0 :(得分:1)
DECLARE @minMonth DATE
SELECT @minMonth = MIN(StartDate) FROM Table1
DECLARE @maxMonth DATE
SELECT @maxMonth = MAX(EndDate) FROM Table1
;WITH CTE_Months AS
(
SELECT @minMonth AS Mnth
UNION ALL
SELECT DATEADD(MM,1,Mnth) FROM CTE_Months
WHERE Mnth<@MaxMonth
)
SELECT Mnth AS Month, COUNT(*) as Members
FROM CTE_Months m
LEFT JOIN Table1 t on m.Mnth BETWEEN t.StartDate AND t.EndDate
GROUP BY Mnth
<强> SQLFiddle Demo 强>
CTE将查找从最小StartDate到最大EndDate的所有月份,如果您需要不同的最小值和最大值,只需更改@MinMonth和@MaxMonth的方式
如果您不希望在可能没有任何成员的月份显示零,请在结尾处将LEFT JOIN替换为INNER。
答案 1 :(得分:1)
给定一个看起来像这样的成员表:
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
然后通过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()
应用于作为判别函数的案例表达式,返回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 )
容易!
答案 2 :(得分:0)
select t.Month, count(*) Members
from (
select case
when startdate <= to_date('01-Jan-2002', 'DD-MON-YYYY') AND enddate >= to_date('31-Jan-2002', 'DD-MON-YYYY') then ' Jan'
when startdate <= to_date('01-Feb-2002', 'DD-MON-YYYY') AND enddate >= to_date('28-Feb-2002', 'DD-MON-YYYY') then ' Feb'
when startdate <= to_date('01-Mar-2002', 'DD-MON-YYYY') AND enddate >= to_date('31-Mar-2002', 'DD-MON-YYYY') then ' Mar'
when startdate <= to_date('01-Apr-2002', 'DD-MON-YYYY') AND enddate >= to_date('30-Apr-2002', 'DD-MON-YYYY') then ' Apr'
when startdate <= to_date('01-May-2002', 'DD-MON-YYYY') AND enddate >= to_date('31-May-2002', 'DD-MON-YYYY') then ' May'
when startdate <= to_date('01-Jun-2002', 'DD-MON-YYYY') AND enddate >= to_date('30-Jun-2002', 'DD-MON-YYYY') then ' Jun'
when startdate <= to_date('01-Jul-2002', 'DD-MON-YYYY') AND enddate >= to_date('31-Jul-2002', 'DD-MON-YYYY') then ' Jul'
when startdate <= to_date('01-Aug-2002', 'DD-MON-YYYY') AND enddate >= to_date('31-Aug-2002', 'DD-MON-YYYY') then ' Aug'
when startdate <= to_date('01-Sep-2002', 'DD-MON-YYYY') AND enddate >= to_date('30-Sep-2002', 'DD-MON-YYYY') then ' Sep'
when startdate <= to_date('01-Oct-2002', 'DD-MON-YYYY') AND enddate >= to_date('31-Oct-2002', 'DD-MON-YYYY') then ' Oct'
when startdate <= to_date('01-Nov-2002', 'DD-MON-YYYY') AND enddate >= to_date('30-Nov-2002', 'DD-MON-YYYY') then ' Nov'
when startdate <= to_date('01-Dec-2002', 'DD-MON-YYYY') AND enddate >= to_date('31-Dec-2002', 'DD-MON-YYYY') then ' Dec'
end as Month
from member) t
group by t.Month
member
是表名。