• your Tally kills the rCTE and hybrid Cursor/Numbers as you can see. On my box:

    Inline Tally = 146 milliseconds

    rCTE = 2113 milliseconds

    Cursor/Numbers = 1183 milliseconds

    The Cursor based solution is only partially so, I still couldn't bring myself to copy the author's while inner loop...yikes. But the point I'm trying to make is with bashing cursors:

    http://stackoverflow.com/questions/287445/why-do-people-hate-sql-cursors-so-much

    see most popular answer under The Size Issue heading.

    --If the table exists, drop it.

    if exists (select * from sys.tables

    where name = 'abc'

    and Schema_Name(schema_id) = 'dbo'

    and [type] = 'U')

    drop table dbo.abc

    go

    --Create the source table, dbo.abc.

    create table dbo.abc

    (SeqNo smallint

    ,Date_Field datetime

    ,Month_Count int

    ,Payment decimal(10,2))

    go

    --Populate the source table, dbo.abc

    insert into dbo.abc (SeqNo, Date_Field, Month_Count, Payment)

    values (1, '20090101', 95000, 100)

    ,(2, '20100101', 7000, 200)

    ,(3, '20110101', 5000, 300)

    go

    --If exists, drop the destination table dbo.def

    if exists (select * from sys.tables

    where name = 'def'

    and Schema_NAME(schema_id) = 'dbo'

    and [type] = 'U')

    drop table dbo.def

    go

    --Create the destination table, dbo.def

    create table dbo.def

    (SeqNo smallint

    ,Date_Field smalldatetime

    ,Payment decimal(10,2))

    go

    ------------ Inline Tally version ------------------

    -- black hole variables:

    DECLARE @SeqNo SMALLINT, @Date_Field DATETIME, @Payment DECIMAL (10,2)

    DECLARE @StartTime DATETIME;

    -- time store

    SET @StartTime = GETDATE()

    ;WITH E1(n) AS (

    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL

    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL

    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1), -- 10 rows

    E2(n) AS (SELECT 1 FROM E1 a, E1 b),-- 10x10 rows

    E3(n) AS (SELECT 1 FROM E2 a, E2 b, E2 c)-- 100x100x100 rows

    SELECT

    @SeqNo = SeqNo,

    @Date_Field = DATEADD(MONTH,x.n,Date_Field),

    @Payment = Payment

    FROM dbo.abc

    CROSS APPLY(SELECT TOP(Month_Count) n = -1+ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E3) x

    SELECT 'Inline Tally = ' + CAST(DATEDIFF(MS,@StartTime,GETDATE()) AS VARCHAR) + ' milliseconds'

    PRINT '-------------------------------------------------------------------------'

    -------------- rCTE version ------------------------------

    SET @StartTime = GETDATE()

    ;with CTE_Base (SeqNo, Date_Field, Month_Count, Payment, Begin_Date, End_Date, Frequency)

    as

    (select SeqNo, Date_Field, Month_Count, Payment, Date_Field, dateadd(mm, Month_Count-1, Date_Field), 1 from dbo.abc

    union all

    select SeqNo, dateadd(mm, Frequency, Date_Field), Month_Count, Payment, Begin_Date, End_Date, Frequency

    from CTE_Base

    where dateadd(mm, Frequency, Date_Field) between Begin_Date and End_Date)

    select

    @SeqNo = SeqNo,

    @Date_Field = Date_Field,

    @Payment = Payment

    from CTE_Base

    where Date_Field between Begin_Date and End_Date

    order by SeqNo, Date_Field

    OPTION(MAXRECURSION 0)

    SELECT 'rCTE = ' + CAST(DATEDIFF(MS,@StartTime,GETDATE()) AS VARCHAR) + ' milliseconds'

    PRINT '-------------------------------------------------------------------------'

    -------------- CURSOR version ------------------------------

    SET @StartTime = GETDATE()

    declare @l_SeqNo int

    ,@l_date_field DATETIME

    ,@l_Month_Count int

    ,@l_Payment decimal(10, 2)

    ,@l_counter SMALLINT

    ,@l_max_month_count INT = (SELECT MAX(Month_Count) FROM dbo.abc);

    SELECT TOP (@l_max_month_count) IDENTITY(int,1,1) AS number

    into #Numbers

    FROM sys.objects s1

    CROSS JOIN sys.objects s2

    select @l_counter = 0

    set nocount on;

    declare i_Cursor CURSOR FAST_FORWARD

    FOR

    select SeqNo, Date_Field, Month_Count, Payment from dbo.abc

    open i_Cursor

    fetch next from i_Cursor into

    @l_SeqNo

    ,@l_date_field

    ,@l_Month_Count

    ,@l_Payment

    while @@fetch_status = 0

    begin

    select number, dateadd(mm, number-1, @l_date_field), @l_Payment

    FROM #Numbers

    WHERE number<=@l_Month_Count

    fetch next from i_Cursor into

    @l_SeqNo

    ,@l_date_field

    ,@l_Month_Count

    ,@l_Payment

    end

    close i_Cursor

    deallocate i_Cursor

    set nocount off;

    SELECT 'Cursor/Numbers = ' + CAST(DATEDIFF(MS,@StartTime,GETDATE()) AS VARCHAR) + ' milliseconds'

    PRINT '-------------------------------------------------------------------------'

    DROP TABLE #Numbers;