Generating a table with records with all possible values from frequency and interval columns

  • Griffster (4/20/2009)


    Lynn Pettis (4/15/2009)


    Here is some code for you to examine and play with. I hope it helps.

    declare @StartDate datetime,

    @EndDate datetime;

    select

    @StartDate = '2009-05-01',

    @EndDate = '2009-11-01';

    with

    CTE (Number) as

    (select 1 union

    select 2 union

    select 3 union

    select 4 union

    select 5 union

    select 6 union

    select 7 union

    select 8 union

    select 9 union

    select 10)

    ,CTE2 (Number) as

    (select c1.Number

    from CTE c1

    cross join CTE c2)

    ,Tally (N) as

    (select row_number() over (order by c3.Number)

    from CTE2 c3

    cross join CTE2 c4)

    select

    ref_no,

    amt_prom_pay,

    case pay_sched_code

    when 'M' then dateadd(mm, (N - 1) * units_pay_sched, prom_to_pay_date)

    when 'D' then dateadd(dd, (N - 1) * units_pay_sched, prom_to_pay_date)

    end as PayDate

    from

    #IntervalData id

    cross join Tally t

    where

    case pay_sched_code

    when 'M' then dateadd(mm, (N - 1) * units_pay_sched, prom_to_pay_date)

    when 'D' then dateadd(dd, (N - 1) * units_pay_sched, prom_to_pay_date)

    end between @StartDate and @EndDate

    order by

    ref_no,

    case pay_sched_code

    when 'M' then dateadd(mm, (N - 1) * units_pay_sched, prom_to_pay_date)

    when 'D' then dateadd(dd, (N - 1) * units_pay_sched, prom_to_pay_date)

    end;

    That seems to have worked great although, I don't understand it really...lol. Could you explain the principles here for me please?

    Griffster (4/20/2009)


    andrewd.smith (4/15/2009)


    This query appears to reproduce your expected results.

    It uses a Tally / Numbers table with name Tally that includes the value 0 (zero).

    There is a pre-query to retrieve the minimum and maximum values (@MinN int, @MaxN) to use from the Tally table in the subsequent query. This is intended to improve performance by limiting the number of rows on which the "complex" date calculation is performed.

    DECLARE @MinN int, @MaxN int

    DECLARE @StartDate datetime, @EndDate datetime

    SELECT

    @StartDate = '20090501',

    @EndDate = '20091101'

    SELECT

    @MaxN = MAX(

    CASE WHEN (TD.prom_to_pay_date <= @EndDate) THEN

    CASE TD.pay_sched_code

    WHEN 'M' THEN DATEDIFF(month, TD.prom_to_pay_date, @EndDate)

    WHEN 'W' THEN DATEDIFF(week, TD.prom_to_pay_date, @EndDate)

    WHEN 'D' THEN DATEDIFF(day, TD.prom_to_pay_date, @EndDate)

    ELSE NULL

    END

    ELSE NULL

    END / TD.units_pay_sched + 1),

    @MinN = MIN(

    CASE WHEN (TD.prom_to_pay_date <= @StartDate) THEN

    CASE TD.pay_sched_code

    WHEN 'M' THEN DATEDIFF(month, TD.prom_to_pay_date, @StartDate)

    WHEN 'W' THEN DATEDIFF(week, TD.prom_to_pay_date, @StartDate)

    WHEN 'D' THEN DATEDIFF(day, TD.prom_to_pay_date, @StartDate)

    ELSE NULL

    END

    ELSE 0

    END / TD.units_pay_sched)

    FROM #IntervalData TD

    SELECT

    TD.ref_no AS Ref_No,

    CASE TD.pay_sched_code

    WHEN 'M' THEN DATEADD(month, T.N * TD.units_pay_sched, TD.prom_to_pay_date)

    WHEN 'W' THEN DATEADD(week, T.N * TD.units_pay_sched, TD.prom_to_pay_date)

    WHEN 'D' THEN DATEADD(day, T.N * TD.units_pay_sched, TD.prom_to_pay_date)

    ELSE NULL

    END AS Payment_date,

    amt_prom_pay AS Amount

    FROM dbo.Tally T

    CROSS JOIN #IntervalData TD

    WHERE (T.N BETWEEN @MinN AND @MaxN)

    AND (

    CASE TD.pay_sched_code

    WHEN 'M' THEN DATEADD(month, T.N * TD.units_pay_sched, TD.prom_to_pay_date)

    WHEN 'W' THEN DATEADD(week, T.N * TD.units_pay_sched, TD.prom_to_pay_date)

    WHEN 'D' THEN DATEADD(day, T.N * TD.units_pay_sched, TD.prom_to_pay_date)

    ELSE NULL

    END

    ) BETWEEN @StartDate AND @EndDate

    ORDER BY Ref_No, Payment_date

    This crashes as there's no Tally table defined. How did you set this up please?

    Search this site for Tally Table. You should find an article written by Jeff Moden that will discribe it quite well. Also, the query I provided has the Tally table included dynamically using CTE's.

    The best way to explain my code would be to have you ask specific questions regarding the code. I'd also suggest reading Books Online about CTE's and the different types of joins. That will give you some background that will make the explainations easier to understand.

  • There are many ways to set up a permanent Tally or Numbers table.

    Here's one way that inserts all the integers from 0 to 9999.

    If you wish to use a permanent Tally table you might wish to put it in the master database.

    CREATE TABLE dbo.Tally (

    N int NOT NULL PRIMARY KEY

    )

    GO

    ;WITH CTE (N) AS (

    select 0 union all

    select 1 union all

    select 2 union all

    select 3 union all

    select 4 union all

    select 5 union all

    select 6 union all

    select 7 union all

    select 8 union all

    select 9

    )

    INSERT dbo.Tally (N)

    SELECT C1.N + 10 * (C2.N + 10 * (C3.N + 10 * C4.N))

    FROM CTE C1

    CROSS JOIN CTE C2

    CROSS JOIN CTE C3

    CROSS JOIN CTE C4

  • Analysed the links to the Tally tables etc now and I think I've got to the bottom of the code now thanks. See how the CTE tally table works now too but a bit curious as to why you did it that way rather than something like the following that Jeff Moden uses as an example

    SELECT TOP 11000 --equates to more than 30 years of dates

    IDENTITY(INT,1,1) AS N

    INTO dbo.Tally

    FROM Master.dbo.SysColumns sc1,

    Master.dbo.SysColumns sc2

    Is this dynamic one much quicker may be?

  • One reason I used a dynamic tally table was simple, what if you didn't already have one or you called it by another name such as Numbers? By doing it dynamically, my code was not dependent on it existing and would work properly.

    In addition I have found a dynamic tally table works equally as well as a permanent tally table. To expand this more, I have recently submitted an article to SSWUG (sorry Steve, but I'll keep you posted regarding the article) regarding dynamic tally (or numbers) tables in response to an article in SQL Server Magazine regarding the same. Personally, I don't recommend the method proposed in SQL Server Magazine as it isn't very scalable.

  • Makes sense that.

    Do you have a link I could navigate to to read your article please? Interested in increasing my knowledge of this part of sql.

  • Griffster (4/20/2009)


    Makes sense that.

    Do you have a link I could navigate to to read your article please? Interested in increasing my knowledge of this part of sql.

    Not at this time. I just submitted it this weekend after having it reviewed by a respected source. Once I hear more I will post the information on my blog (SQL Musings from the Desert).

  • Thanks Andrew by the way too. Your query now works with that tally table setup.

  • Ok. I'll set myself a reminder to check the blog out every few days then. Many thanks.

Viewing 8 posts - 16 through 22 (of 22 total)

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