根据持续时间的月数计算不同的成员

时间:2013-04-23 21:14:15

标签: sql sql-server

我有一个包含以下列的成员资格表:

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

3 个答案:

答案 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是表名。