1

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

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
4

3 回答 3

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

然后通过 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 )

简单的!

于 2013-04-23T23:01:33.100 回答
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 演示

CTE 将查找从 min StartDate 到 max EndDate 的所有月份,如果您需要不同的 min 和 max,只需更改获取 @MinMonth 和 @MaxMonth 的方式

如果您不想在可能没有任何成员的月份显示零,请在末尾将 LEFT JOIN 替换为 INNER。

于 2013-04-23T22:00:20.040 回答
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表名在哪里。

于 2013-04-23T21:18:36.490 回答