SQL Server:CTE月份范围之间的月份

时间:2014-04-26 03:39:05

标签: sql sql-server sql-server-2008 tsql

我有一个具有以下结构的表:

StartDate datetime NOT NULL
EndDate datetime NOT NULL
EnrollmentId nvarchar(255) NOT NULL
ProgrammeId nvarchar(255) not null 

EnrollmentId是主键,有点像GUID字符串,ProgrammeId指的是'主题代码',有点像GUID。

我想要做的是确定Programme在所有注册的特定日期范围内累计的总月数,例如从2011年1月1日到2014年4月1日。

因此,假设我在08/08/201001/01/2012之间存在针对特定programmeId的注册。我希望计算01/01/2011之后累积的所有月份。因此,在此日期之前的任何月份都不应计算,即08/08/2010至2010年12月31日不应计算在内。

理想情况下,我希望计算给定日期范围内每ProgrammeId个月的数量。

只是几点说明:

  1. 我需要计算整月,所以如果报名时间从2011年8月1日开始,到2012年1月20日结束,则应考虑仅12个月。
  2. 我想计算月份的时间通常是在相应月份的开始时间,即2011年1月1日至2014年4月1日,2014年5月1日等等。
  3. 一些代码可以返回注册的所有月份:

    ;with MonthList as (
        select 
            DATEADD(month, M, '12/1/1899') as 'FirstDay',
            dateadd(day, -1, dateadd(month, M + 1, '12/1/1899')) as 'LastDay',
            DATEADD(month, M + 1, '12/1/1899') as 'FirstDayNextMonth'
        from (
            select top 3000 ROW_NUMBER() over (order by s.name) as 'M'
            from master..spt_values s) s
    )
    
    select
      t.ProgrammeId, ml.FirstDay, ml.LastDay
    from
        Enrollment t
        inner join MonthList ml
            on  t.startdate < ml.FirstDayNextMonth
                and t.enddate >= ml.FirstDay
    

    提前致谢

2 个答案:

答案 0 :(得分:0)

我以为我有一个答案,但你的更新让我对你真正想要的东西感到困惑。我包括the statement I wrote (SQL Fiddle),希望它会有所帮助。

SELECT ProgrammeId, 
       DATEDIFF(MONTH, 
                 CASE WHEN '2011-01-01 00:00:00' > MIN(StartDate) THEN '2011-01-01 00:00:00'
                      ELSE MIN(StartDate)
                  END,
                 CASE WHEN '2014-04-01 00:00:00' < MAX(EndDate) THEN '2014-04-01 00:00:00'
                      ELSE MAX(EndDate)
                  END
               ) As Months
  FROM ProgrammeEnrollment
 GROUP BY ProgrammeID
;

SQL Server没有GREATESTLEAST,因此我不得不使用CASE语句进行即兴创作。如果您使用的是具有GREATESTLEAST的RDBMS,那么它们将替代CASE语句。

答案 1 :(得分:0)

  1. 您可能需要像Calculating number of full months between two dates in SQL中的功能才能获得完整的月份。
  2. 假设你有类似的东西,我会过滤以确保你在射程中并根据你的建议选择开始和结束以适应CTE的范围。 3.分组和总结很容易。
  3. 设置一些样本数据:

    CREATE TABLE #tbl (
      EnrollmentId INT NOT NULL --NOTE: using INT instead of your VARCHAR becuas eit easier and doesn't matter in sample
     ,ProgrammeId INT NOT NULL
     ,StartDate DATETIME NOT NULL 
     ,EndDate DATETIME NOT NULL
    )
    
    INSERT INTO #tbl VALUES
    (1,1,'2013-01-01','2014-01-01'),
    (2,1,'2013-07-01','2014-01-01'),
    (3,2,'2013-01-01','2014-01-01')
    (4,3,'2013-01-15','2014-03-01')
    

    现在声明搜索范围并进行查询

    DECLARE @RangeStart DATETIME = '2013-01-01'
    DECLARE @RangeEnd DATETIME = '2013-12-01'
    
    ;WITH cte AS (
    SELECT EnrollmentId
          ,ProgrammeId
          ,CASE WHEN @RangeStart >= StartDate THEN @RangeStart ELSE StartDate END EffectStartDate
          ,CASE WHEN @RangeEnd <= EndDate THEN @RangeEnd ELSE EndDate END EffectEndDate
    
      FROM #tbl
     WHERE @RangeStart BETWEEN StartDate AND EndDate --start date is in range
        OR @RangeEnd BETWEEN StartDate AND EndDate --or end date is in range
        OR (EndDate > @RangeEnd AND StartDate < @RangeStart) --or period contains the range
     )
     SELECT ProgrammeId
           ,SUM(dbo.FullMonthsSeparation(EffectStartDate,EffectEndDate)) Months
       FROM cte
      GROUP BY ProgrammeId
    

    样本结果:

    ProgrammeId Months
    ----------- -----------
    1           16
    2           11
    3           10