abinder (5/20/2008)
Matt,That's one of the problems. I don't have a Payments(Credits)Applied table. I have only what I stated in my original posting a series of Debits and Credits and their respective dates. (There is, of course, more info but none that applies here.)
Alfons
All right then - we do it the "hard" way....:)
This has a few moving parts, and is based on the assumption that your tracking your invoices as POSITIVE and your payments are NEGATIVE.
[font="Courier New"]DROP TABLE #cctemp
DROP TABLE #ccbal
DROP TABLE #CCTEMP_original
DROP TABLE #ddtemp
--first some test data
CREATE TABLE #CCTEMP_original(memberID INT, cashflow money, postdate DATETIME)
INSERT #CCTEMP_original
SELECT TOP 1000000
CAST(RAND(checksum(NEWID()))*50+1 AS INT),
CAST(RAND(checksum(NEWID()))*500-250 AS money),
DATEADD(dd,36524+365,CAST(RAND(checksum(NEWID()))*2500 AS INT))
FROM sys.all_columns sc1, sys.all_columns sc2
CREATE CLUSTERED INDEX uci_cctempO ON #CCTEMP_original(memberID,postdate)
--start cleanup process
DECLARE @g DATETIME --just to know how fast this is
SET @g=GETDATE();
--first - a helper table:
SELECT memberID,
SUM(CASE WHEN cashflow>0 THEN cashflow ELSE 0 END) AS debit,
ABS(SUM(CASE WHEN cashflow>0 THEN 0 ELSE cashflow END)) AS credit,
SUM(cashflow) AS bal
INTO #ccbal
FROM #CCTEMP_original
GROUP BY memberID
CREATE UNIQUE CLUSTERED INDEX uci_ccb ON #ccbal(memberID)
--move in only those items that have more debits than credits
--if you have a credit balance you don't belong on an aging report
--we only care about the debits
SELECT *, CAST(0 AS money) AS runbal
INTO #cctemp
FROM #CCTEMP_original
WHERE EXISTS (SELECT NULL FROM #ccbal WHERE #ccbal.memberid=#CCTEMP_original.memberID AND bal>0)
AND cashflow>0
CREATE CLUSTERED INDEX uci_cct ON #cctemp(memberid, postdate)
--create a running grouped total of the debits:
DECLARE @prevMemberID INT
SET @prevMemberID=-1;
DECLARE @runbal money
SET @runbal =0;
DECLARE @dummy money
SET @dummy=0;
UPDATE #CCTEMP
SET @runbal=runbal=CASE WHEN @prevmemberID=memberid THEN @runbal ELSE 0 END + cashflow,
@dummy=@runbal,
@prevmemberID=memberID
FROM #CCTEMP WITH (INDEX(uci_cct),tablockX)
--get rid of all of the items where the running total is less than the total debits
--those we be all of the debits that have been "paid off"
DELETE #cctemp
FROM #cctemp
INNER JOIN #ccbal ON #cctemp.memberID=#ccbal.memberID
WHERE runbal<credit
--update the cashflow of the first row by group to account for any remaining credit
;WITH balCTE AS
(
SELECT #cctemp.memberid,
Row_Number() OVER (partition BY #cctemp.memberID ORDER BY #cctemp.memberID,runbal) RN,
cashflow,
runbal,
credit
FROM #cctemp
INNER JOIN #ccbal ON #cctemp.memberiD=#ccbal.memberID)
UPDATE balCTE
SET cashflow=runbal-credit
WHERE RN=1
--create the aging data
SELECT MemberID, SUM(CashFlow) CashFlow, DATEDIFF(DAY,postDate + 1,GETDATE())/15*15 DaysAged
INTO #ddtemp
FROM #cctemp
GROUP BY MemberID, DATEDIFF(DAY,postDate + 1,GETDATE())/15*15 ORDER BY MemberID
--show me the magic
SELECT * FROM #DDtemp
ORDER BY memberID, daysaged DESC
--how fast were we?
SELECT DATEDIFF(ms,@g,GETDATE())
--play these if you want validation we did it right
--select sum(cashflow) from #CCTEMP_original
--select sum(bal) from #CCbal
--select sum(case when bal>0 then bal else 0 end) from #CCbal
--select sum(cashflow) from #cctemp[/font]
I'm thinking this should do the trick.
considering how much we're doing - 4.4 seconds doesn't seem so bad on the million row test....
----------------------------------------------------------------------------------
Your lack of planning does not constitute an emergency on my part...unless you're my manager...or a director and above...or a really loud-spoken end-user..All right - what was my emergency again?