如何合并此代码以避免多个#temp表写入?

时间:2013-11-01 17:27:43

标签: tsql sql-server-2012

虽然这段代码工作正常但效率不高 - 大量使用临时表,我宁愿合并。对于有兴趣使用示例数据的任何人,都包含create sumtest表和写入它的insert语句。

        CREATE TABLE SumTest(
            POSDate datetime NULL,
            debit money NULL,
            credit money NULL,
            RollingBalance money NULL,
            expires datetime NULL,
            ExpiringDebitBalance_2013 money,
            ExpiringDebitBalance_2014 money
        );

        -- mimic actual sales transactions

        insert into sumtest (POSDate,debit,credit,expires) values ('Jan  5 2013 12:00AM','670.00','22.00','Dec 31 2014 12:00AM');
        insert into sumtest (POSDate,debit,credit,expires) values ('Jan  6 2013 12:00AM','821.00','0.00','Dec 31 2014 12:00AM');
        insert into sumtest (POSDate,debit,credit,expires) values ('Mar  8 2013 12:00AM','62.00','700.00','Dec 31 2014 12:00AM');
        insert into sumtest (POSDate,debit,credit,expires) values ('Mar 11 2013 12:00AM','78.00','29.00','Dec 31 2014 12:00AM');
        insert into sumtest (POSDate,debit,credit,expires) values ('Mar 11 2013 12:00AM','900.00','87.00','Dec 31 2014 12:00AM');
        insert into sumtest (POSDate,debit,credit,expires) values ('Apr 16 2013 12:00AM','0.00','440.00',NULL);
        insert into sumtest (POSDate,debit,credit,expires) values ('Aug 18 2013 12:00AM','0.00','50.00',NULL);
        insert into sumtest (POSDate,debit,credit,expires) values ('Aug 19 2013 12:00AM','470.00','200.00','Dec 31 2014 12:00AM');
        insert into sumtest (POSDate,debit,credit,expires) values ('Dec 31 2012 12:00AM','1000.00','200.00','Dec 31 2013 12:00AM');
        insert into sumtest (POSDate,debit,credit,expires) values ('Dec 22 2013 12:00AM','200.00','0.00','Dec 31 2014 12:00AM');
        insert into sumtest (POSDate,debit,credit,expires) values ('Dec 20 2013 12:00AM','500.00','0.00','Dec 31 2014 12:00AM');
        insert into sumtest (POSDate,debit,credit,expires) values ('Nov 10 2012 12:00AM','200.00','0.00','Dec 31 2013 12:00AM');
        insert into sumtest (POSDate,debit,credit,expires) values ('Nov 11 2012 12:00AM','150.00','0.00','Dec 31 2013 12:00AM');
        insert into sumtest (POSDate,debit,credit,expires) values ('Nov 15 2012 12:00AM','0.00','100.00',NULL);

      -- // ---------- REAL CODE BEGINS -------------------------- // --

        create table #work
        (   
            POSYear int NULL,
            POSMonth int NULL,
            debit money NULL,
            credit money NULL,
            RollingBalance money NULL,
            expires datetime
        )

        -- start with ditching the time

        insert into #work
            (POSYear,POSMonth,debit,credit,expires)
        select datepart(Year, POSDate) as POSYear
                ,datepart(Month, POSDate) as POSMonth
                ,debit
                ,credit
                ,expires
        from sumtest



        -- dump an ordered set by year,month with a rolling balance of all points/redemptions

        ;with distilled as
            (select POSYear,
                    POSMonth,
                    TotalDebit = sum(debit),
                    TotalCredit = sum(credit),
                    expires
            from #work
            group by POSYear,POSMonth,expires
            )

        select POSYear,
                POSMonth,
                TotalDebit,
                TotalCredit,
                RollingBalance =  sum(totaldebit + (totalcredit)*-1) over ( order by POSYear, POSMonth),
                expires
        into #work2
        from distilled
        order by POSYear,POSMonth,TotalDebit,TotalCredit



        -- filter out credits lines that dont have an expiration date
                    -- Note: I would rather not do this--if there is a better way
                    --       to sum up by yyyy,mm here to consolidate to 
                    --       single rows please advise. 

        select x.POSYear,
                x.POSMonth,
                sum(x.TotalDebit) as TotalDebit,
                sum(x.TotalCredit) as TotalCredit,
                x.RollingBalance,
                EXPYear = datepart(Year, y.expires),
                EXPMonth = datepart(Month, y.expires)
        into #work3
        from #work2 x
            inner join #work2 y on x.posyear = y.posyear
                            and x.posmonth = y.posmonth
        where y.expires is not null
        group by x.posyear,x.posmonth,x.rollingbalance,y.expires
        order by posyear,posmonth

        -- add back in lines that are single credits, w/o debits
                    -- Note: I like this even less! :-\

        INSERT INTO #work3
            (POSYear,POSMonth,TotalDebit,TotalCredit,RollingBalance,EXPYear,EXPMonth)
        select 
            POSYear,
            POSMonth,
            TotalDebit,
            TotalCredit,
            RollingBalance,
        case 
            when expires is null then (POSYear + 1)
        end as EXPYear,
        case 
            when expires is null then 12
        end as EXPMonth
        from #work2
        where (POSYear + POSMonth) NOT IN (
                    select POSYear + POSMonth
                    from #work3);

2 个答案:

答案 0 :(得分:1)

似乎不需要#work可以替换为

;with distilled as
(
    select POSYear,
            POSMonth,
            TotalDebit = sum(debit),
            TotalCredit = sum(credit),
            expires
    from (
        select datepart(Year, POSDate) as POSYear
                ,datepart(Month, POSDate) as POSMonth
                ,debit
                ,credit
                ,expires
        from sumtest
    )
    group by POSYear,POSMonth,expires
)

select POSYear,
    POSMonth,
    TotalDebit,
    TotalCredit,
    RollingBalance =  sum(totaldebit + (totalcredit)*-1) over ( order by POSYear, POSMonth),
    expires
into #work2
from distilled

我认为最后两个查询可以简化为

select x.POSYear,
        x.POSMonth,
        sum(case when expires is null then 0 else x.TotalDebit end) as TotalDebit,
        sum(case when expires is null then 0 else x.TotalCredit end) as TotalCredit,
        x.RollingBalance,
        EXPYear = case when expires is null then (POSYear + 1)
                        else datepart(Year, y.expires)
                    end,
        EXPMonth = case when expires is null then 12
                         else datepart(Month, y.expires)
                    end
into #work3
from #work2 x
inner join #work2 y on x.posyear = y.posyear
                   and x.posmonth = y.posmonth
group by x.posyear,x.posmonth,x.rollingbalance,y.expires

您似乎也不需要order任何事情

答案 1 :(得分:0)

好的,这是精炼版 - 没有临时表。

         SELECT
                POSYear
              , POSMonth
              , TotalDebit AS TotalEarned
              , TotalCredit AS TotalRedeemed
              , RollingBalance
              , EXPYear
              , EXPMonth
              , case 
                  when EXPYear = 2013 then RollingBalance 
                else 0 
                end as EXPRollingBal_2013
              , case 
                  when prevEXPYear <> EXPYear then lag(RollingBalance,1) over (ORDER BY POSYear ,POSMonth) - TotalCredit
                end as "EXPCarryForwardBal"   -- // Note: This only works for the first instance AFTER then new year. Wrong!
              , case 
                  -- when EXPYear = 2014 then RollingBalance  // Note: This is WRONG, zero it out for now.
                  when EXPYear = 2014 then 0.00
                   else 0 
                end as EXPRollingBal_2014
            INTO #baseExpiring
           FROM (
                  SELECT
                      POSYear
                    , POSMonth
                    , TotalDebit
                    , TotalCredit
                    , sum(TotalDebit - TotalCredit) over (ORDER BY POSYear ,POSMonth) AS RollingBalance
                    , EXPYear
                    , EXPMonth
                    , lag(EXPYear,1) over (ORDER BY POSYear ,POSMonth) as prevEXPYear
                   FROM (
                       SELECT
                            year(posdate) AS POSYear
                          , month(posdate) AS POSMonth
                          , sum(debit) AS TotalDebit
                          , sum(credit) AS TotalCredit
                          , CASE 
                              WHEN expires IS NULL THEN year(posdate) + 1 
                                ELSE year(expires) 
                             END AS EXPYear
                          , CASE 
                               WHEN expires IS NULL THEN 12 
                                 ELSE month(expires) 
                            END AS EXPMonth
                          FROM SumTest
                           GROUP BY
                              year(posdate)
                            , month(posdate)
                            , CASE 
                               WHEN expires IS NULL THEN year(posdate) + 1 
                                 ELSE year(expires) 
                               END
                            , CASE WHEN expires IS NULL THEN 12 
                                 ELSE month(expires) 
                              END
                   ) as dataset1  -- // resolve line consolidation when there's a standalone redempton when summed, leaving 2 distinct yyyy,mm rows.
              ) as dataset2  -- // broader resolution of rolling balance w/windowing.
        ;