Table of Dates in TSQL

  • HI,

    I looking to create a tmp table of dates as simply as possible in tsql.

    Examples online take a lot of lines, I'd like to do it in 2 or 3 lines using row number perhaps, but without looping!

    The gist of it is I want to add 4 weeks to a startdate for 1 full year!

    @StartDate

    @EndDAte = DateAdd(Year,1,@StartDate)

    Create #tmpCalendar

    (

    MonthEnd DATE

    ) ;

    --Pop table something like

    INSERT INTO #tmpCalendar

    SELECT @StartDate AS MonthEnd

    UNION ALL

    SELECT DATEADD(Week,4,@StartDate) as MonthEnd

    WHERE DATEADD(Week,4,@StartDate) <= @EndDate

    OR

    INSERT INTO #tmpCalendar

    SELECT DATEADD(Week,4*Row_Number,@StartDate) as MonthEnd

    WHERE DATEADD(Week,4,@StartDate) <= @EndDate

    But I know Im missing something here!

  • SELECT DATEADD(week,Weeks,@StartDate) AS [MonthEnd]

    FROM (VALUES (0),(4),(8),(12),(16),(20),(24),(28),(32),(36),(40),(44),(48),(52)) w (Weeks)

    Far away is close at hand in the images of elsewhere.
    Anon.

  • Hi David,

    That is exactly what I was looking for...

    I wonder tho can it be done recursively!

    kind regards

  • David Burrows (8/9/2016)


    SELECT DATEADD(week,Weeks,@StartDate) AS [MonthEnd]

    FROM (VALUES (0),(4),(8),(12),(16),(20),(24),(28),(32),(36),(40),(44),(48),(32)) w (Weeks)

    Hi David,

    I think you have a typo in your values...shouldn't that 32 at the end be a 52?

    Cheers,


    SELECT quote FROM brain WHERE original = 1
    0 rows returned

  • Y.B. (8/9/2016)


    David Burrows (8/9/2016)


    SELECT DATEADD(week,Weeks,@StartDate) AS [MonthEnd]

    FROM (VALUES (0),(4),(8),(12),(16),(20),(24),(28),(32),(36),(40),(44),(48),(32)) w (Weeks)

    Hi David,

    I think you have a typo in your values...shouldn't that 32 at the end be a 52?

    Cheers,

    Yeah, good spot πŸ™‚

    Far away is close at hand in the images of elsewhere.
    Anon.

  • Tallboy (8/9/2016)


    Hi David,

    That is exactly what I was looking for...

    I wonder tho can it be done recursively!

    kind regards

    WITH

    cte (MonthEnd)

    AS (

    SELECT @StartDate

    UNION ALL

    SELECT DATEADD(week,4,MonthEnd)

    FROM cte

    WHERE DATEADD(week,4,MonthEnd) <= @EndDate

    )

    SELECT MonthEnd

    FROM cte

    Far away is close at hand in the images of elsewhere.
    Anon.

  • Even if with this amount of rows it might make no difference, avoid using recursive CTEs to count. Here's another version which might seem too wordy, but it will be able to perform well under most circumstances.

    DECLARE @StartDate datetime,

    @EndDAte datetime;

    SELECT @StartDate = '20160101',

    @EndDAte = DateAdd(Year,1,@StartDate);

    WITH

    E(n) AS(

    SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0))E(n)

    ),

    E2(n) AS(

    SELECT a.n FROM E a, E b

    ),

    E4(n) AS(

    SELECT a.n FROM E2 a, E2 b

    ),

    cteTally(n) AS(

    SELECT 0 UNION ALL

    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) n

    FROM E4

    )

    SELECT DATEADD(Week,4*n,@StartDate) as MonthEnd

    FROM cteTally

    WHERE DATEADD(Week,4*n,@StartDate) <= @EndDate;

    Of course, if you don't want to write the cascade CTEs each time, you can convert them to an ITV function or a view. You could also create a table with its proper clustered index.

    Luis C.
    General Disclaimer:
    Are you seriously taking the advice and code from someone from the internet without testing it? Do you at least understand it? Or can it easily kill your server?

    How to post data/code on a forum to get the best help: Option 1 / Option 2
  • Luis Cazares (8/9/2016)


    Even if with this amount of rows it might make no difference, avoid using recursive CTEs to count. Here's another version which might seem too wordy, but it will be able to perform well under most circumstances.

    DECLARE @StartDate datetime,

    @EndDAte datetime;

    SELECT @StartDate = '20160101',

    @EndDAte = DateAdd(Year,1,@StartDate);

    WITH

    E(n) AS(

    SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0))E(n)

    ),

    E2(n) AS(

    SELECT a.n FROM E a, E b

    ),

    E4(n) AS(

    SELECT a.n FROM E2 a, E2 b

    ),

    cteTally(n) AS(

    SELECT 0 UNION ALL

    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) n

    FROM E4

    )

    SELECT DATEADD(Week,4*n,@StartDate) as MonthEnd

    FROM cteTally

    WHERE DATEADD(Week,4*n,@StartDate) <= @EndDate;

    Of course, if you don't want to write the cascade CTEs each time, you can convert them to an ITV function or a view. You could also create a table with its proper clustered index.

    Nice Luis

    I originally thought along those lines but chose mine as the original request was restricted to a year and requested in 2 or 3 lines πŸ˜‰

    Far away is close at hand in the images of elsewhere.
    Anon.

  • Luis Cazares (8/9/2016)


    Even if with this amount of rows it might make no difference, avoid using recursive CTEs to count generate rows...

    ...because they're way too expensive for such a simple process. CTE's are great for resolving hierarchies and for complex counting processes including running totals and moving averages.

    β€œWrite the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.” - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

  • ChrisM@Work (8/9/2016)


    Luis Cazares (8/9/2016)


    Even if with this amount of rows it might make no difference, avoid using recursive CTEs to count generate rows...

    ...because they're way too expensive for such a simple process. CTE's are great for resolving hierarchies and for complex counting processes including running totals and moving averages.

    Totally agree, thanks for clarifying that. πŸ˜‰

    Luis C.
    General Disclaimer:
    Are you seriously taking the advice and code from someone from the internet without testing it? Do you at least understand it? Or can it easily kill your server?

    How to post data/code on a forum to get the best help: Option 1 / Option 2
  • Here is technically a two line solution (which can be only 1 if you don't use variables)... πŸ˜‰

    DECLARE @startDate DATE = '20160101', @skipWeeks TINYINT = 4, @maxWeeks TINYINT = 54

    SELECT DATEADD(week, number, @startDate) AS myDates FROM master.dbo.spt_values WHERE number <= @maxWeeks AND number % @skipWeeks = 0 AND Type = 'p'

    NOTE: This is using an undocumented table and the max value can't exceed 2047.

    EDIT: Personally, I would just use a tally table to generate the numbers it's better for a few extra 'lines'. Also instead of hardcoding values you can easily change the variables to output your dates any way you want. Just keep in mind you'll have cross join more values the higher you want your max weeks to go to.

    DECLARE @startDate DATE = '20160101', @skipWeeks TINYINT = 4, @maxWeeks TINYINT = 54

    ;WITH cte(n) AS (

    SELECT TOP (@maxWeeks) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))-1

    FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)

    CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)

    )

    SELECT DATEADD(week, n, @startDate) AS myDates FROM cte WHERE n <= @maxWeeks AND n % @skipWeeks = 0


    SELECT quote FROM brain WHERE original = 1
    0 rows returned

  • Thats all true,

    But I find a simple example is best when demonstrating/explaining complex functionality such as recursion etc.

    Thanks Again guys

  • One last thing. Questions often come up where a count of days is needed excluding special days. You might find it handy to add a column to flag whether or not a day is a holiday or a weekend or a workday. Having such a column makes such queries much simpler.

    It's easy to flag weekend days when you create the table. After that, you can update it with 'H' for holidays at your leisure.

    ;with tally (N) as (select top(10000) ROW_NUMBER() over(order by (select null)) from sys.columns cross join sys.objects)

    select N as DateID, DATEADD(d,N,convert(date,'12/31/2015')) as MyDate

    ,case when DATEPART(dw,DATEADD(d,N,convert(date,'12/31/2015'))) in (1,7) then 'W' else '' end as SpecialDay

    into #MyDates

    from tally

    create unique clustered index PK_#MyDates on #MyDates(MyDate)

    -- example of counting days excluding weekends

    declare @start date = '5/1/2016'

    ,@end date = '5/30/2016'

    select sum(1) as Workdays

    from #MyDates

    where Mydate between @start and @end

    AND specialday <> 'W'

    Good luck.

    __________________________________________________

    Against stupidity the gods themselves contend in vain. -- Friedrich Schiller
    Stop, children, what's that sound? Everybody look what's going down. -- Stephen Stills

Viewing 13 posts - 1 through 12 (of 12 total)

You must be logged in to reply to this topic. Login to reply