需要从TSQL中的2个日期列的范围中提取一组日期行

时间:2014-05-12 10:40:51

标签: sql sql-server tsql

我有这张桌子:

Year    Holiday     HolidayStart  HolidayEnd
2008    Holiday1    09/09/2008    30/09/2008
2008    Holiday2    01/10/2008    21/10/2008
2008    Holiday3    22/10/2008    12/11/2008
2008    Holiday4    01/12/2008    21/12/2008
2008    Holiday5    02/01/2008    22/01/2008
2008    Holiday6    01/03/2008    21/03/2008
2008    Holiday7    23/03/2008    20/04/2008
2008    Holiday8    27/04/2008    16/05/2008

我想转换它,以便我每个日期都有一行,比如

Year    Holiday     Dates of the holiday
2008    Holiday1    09/09/2008
2008    Holiday1    10/09/2008
2008    Holiday1    11/09/2008
2008    Holiday1    12/09/2008

2008    Holiday2    01/10/2008
2008    Holiday2    02/10/2008
2008    Holiday2    03/10/2008
2008    Holiday2    04/10/2008

我该怎么做?

2 个答案:

答案 0 :(得分:1)

您似乎有额外的过滤要求(因为Holiday1Holiday2中显然有22天),但您可以通过预测涵盖整个范围的连续日期范围来实现此目的将Holidays表插入到其中:

WITH CTENumbers AS
(
   SELECT 0 AS Number
   UNION ALL
   SELECT Number + 1
   FROM CTENumbers
   WHERE Number < 300
),
DateRanges AS
(
   SELECT
      MIN(HolidayStart) AS MinStart,
      MAX(HolidayStart) AS MaxStart
   FROM Holidays
)
SELECT YEAR, Holiday, DATEADD(dd, cte.Number, dr.MinStart) AS DateOfTheHoliday
   FROM
      CTENumbers cte,
      DateRanges dr,
      Holidays h
WHERE 
   DATEADD(dd, cte.Number, dr.MinStart) 
         BETWEEN h.HolidayStart AND h.HolidayEnd
   AND Holiday IN ('Holiday1', 'Holiday2')
ORDER BY HolidayStart
OPTION( MAXRECURSION 1000)      

SqlFiddle here

如果您需要定期执行此操作,我建议您在永久表中保留日期范围(由CTE预测为偏离开始日期的偏移量)。

答案 1 :(得分:0)

这有效,虽然它有点倒退使用这样的SQL。第一个代码块创建了一些临时表来演示解决方案。第二个块是实际的解决方案。

CREATE TABLE #demo (
[year] int ,
holiday nvarchar( 50 ) ,
holidaystart date ,
holidayend date );

INSERT INTO #demo ( [year] ,
                    holiday ,
                    holidaystart ,
                    holidayend )
VALUES
( 2008 ,
  'Holiday1' ,
  '2008-09-09' ,
  '2008-09-30' ) ,
( 2008 ,
  'Holiday2' ,
  '2008-10-01' ,
  '2008-10-21' ) ,
( 2008 ,
  'Holiday3' ,
  '2008-10-22' ,
  '2008-11-12' );

CREATE TABLE #result (
[year] int ,
holiday nvarchar( 50 ) ,
holidaydate date );

这是为您提供数据的实际代码,首先使用顶部来创建演示数据。

SELECT ROW_NUMBER( ) OVER( ORDER BY holidaystart ) rownum ,
       holiday ,
       holidaystart ,
       holidayend ,
       [year]
INTO #foreach_item
  FROM #demo;

DECLARE
   @Iter int = ( SELECT MIN( rownum )
                   FROM #foreach_item );
DECLARE
   @Max int = ( SELECT MAX( rownum )
                  FROM #foreach_item );

WHILE @Iter <= @Max

    BEGIN
        DECLARE
           @First_Day date = DATEADD( dd , -1 , ( SELECT holidaystart
                                                    FROM #foreach_item
                                                    WHERE rownum = @Iter ));
        DECLARE
           @Last_Day date = ( SELECT holidayend
                                FROM #foreach_item
                                WHERE rownum = @Iter );
        DECLARE
           @Holiday nvarchar( 50 ) = ( SELECT holiday
                                         FROM #foreach_item
                                         WHERE rownum = @Iter );
        WHILE @First_Day < @Last_Day
            BEGIN

                INSERT INTO #result
                SELECT [year] ,
                       holiday ,
                       DATEADD( dd , 1 , @First_Day )
                  FROM #foreach_item
                  WHERE holiday = @Holiday;
                SET @First_Day = DATEADD( dd , 1 , @First_Day );
            END;
        SET @Iter = @Iter + 1;
    END;

SELECT *
  FROM #result
  ORDER BY holiday;