July 25, 2016 at 7:54 am
Firstly, this is a system that I have inherited.
I have a cursor that pulls out a set of ids and amounts. The data is as follows:
ID | Amount | Status
-----------------------------------------
1 | 15.00 | Paid
2 | 3.00 | Paid
3 | 10.00 | Awaiting
4 | 12.00 | Awaiting
The cursor loops and adds up the amounts until it reaches a set value, in this case we will use 20.00
What I need to replicate is the following:
The cursor, is holding a running total, when this running total goes over 20, then, as in the example above i need to do the following:
ID | Amount | Status
-----------------------------------------
1 | 15.00 | Paid
2 | 3.00 | Paid
3 | 8.00 | Paid
4 | 12.00 | Cancelled
5 | 8.00 | Brought Forward
As you can see, ID 3 in now showing as a part payment where the remaining balance over 20 becoming a brought forward payment ID 5.
On top of this any outstanding payments have become cancelled, Id 4.
Must I use cursors or is there a better way?
July 25, 2016 at 8:18 am
There are 2 other ways that might possibly help you.
One is to use the window functions capabilities introduced in SQL Server 2012: http://sqlmag.com/sql-server-2012/how-use-microsoft-sql-server-2012s-window-functions-part-1
The second one is to use the Quirky update which is explained and compared in here: http://www.sqlservercentral.com/articles/T-SQL/68467/
If you don't feel comfortable with these methods yet, you can start by correctly defining the cursors to improve their performance: http://sqlblog.com/blogs/hugo_kornelis/archive/2007/11/21/curious-cursor-optimization-options.aspx
http://sqlperformance.com/2012/09/t-sql-queries/cursor-options
July 25, 2016 at 10:03 am
Oh, inherited mess... my favorite! Here's a quick example of using a window function.
SELECT pmts.ID
, pmts.Amount
, pmts.[Status]
, SUM([Amount]) OVER (ORDER BY ID
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW) AS RunAmt
FROM
(SELECT 1 AS ID
, 15 AS Amount
, 'Paid' As [Status]
UNION ALL
SELECT 2, 3, 'Paid'
UNION ALL SELECT 3,10,'Awaiting'
UNION ALL
SELECT 4, 12,'Awaiting') pmts;
If you wanted to do a GROUP BY in your running total, it would be like this (note the PARTITION BY):
SELECT pmts.OwnerID
, pmts.ID
, pmts.Amount
, pmts.[Status]
, SUM([Amount]) OVER (PARTITION BY OwnerID ORDER BY ID
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW) AS RunAmt
FROM
(SELECT 'a' AS OwnerID
, 1 AS ID
, 15 AS Amount
, 'Paid' As [Status]
UNION ALL
SELECT 'a',2, 3, 'Paid'
UNION ALL SELECT 'a',3,10,'Awaiting'
UNION ALL
SELECT 'b',4, 12,'Awaiting') pmts;
July 25, 2016 at 6:57 pm
http://sqlperformance.com/2012/07/t-sql-queries/running-totals
http://sqlperformance.com/2014/01/t-sql-queries/grouped-running-totals
Note that Aaron does NOT do the Quirky Update correctly in his stuff. But with 2012 Windowing Functions are the way to go in any case, at least when done properly (i.e. with ROWS and not RANGE).
Best,
Kevin G. Boles
SQL Server Consultant
SQL MVP 2007-2012
TheSQLGuru on googles mail service
July 25, 2016 at 10:07 pm
Paul Giles-Randall (7/25/2016)
Firstly, this is a system that I have inherited.I have a cursor that pulls out a set of ids and amounts. The data is as follows:
ID | Amount | Status
-----------------------------------------
1 | 15.00 | Paid
2 | 3.00 | Paid
3 | 10.00 | Awaiting
4 | 12.00 | Awaiting
Quick questions, how large is your data set?
a) Distinct count of IDs
b) Number of transactions in total
c) Number of periods
😎
July 26, 2016 at 8:45 pm
Paul Giles-Randall (7/25/2016)
What I need to replicate is the following:The cursor, is holding a running total, when this running total goes over 20, then, as in the example above i need to do the following:
ID | Amount | Status
-----------------------------------------
1 | 15.00 | Paid
2 | 3.00 | Paid
3 | 8.00 | Paid
4 | 12.00 | Cancelled
5 | 8.00 | Brought Forward
I guess it must be
3 | 2.00 | Paid
Then it would make sense.
_____________
Code for TallyGenerator
July 26, 2016 at 8:56 pm
I guess there is no point of getting rid of the cursor.
You may mark a payment "Paid" only after the payment has been actually made.
And I bet the payments are made one by one.
Which means using some sort of a cursor (loop) anyway.
What you need to do is within the loop, when the next "Awaiting" payment is picked up for processing calculate the already paid amount (SUM where Status = 'Paid'), see if adding the current one will go over the limit and if yes - split it accordingly into "Paid" and "Brought forward" parts.
All other "Awaiting" to be marked "Cancelled" (UPDATE where Status = 'Awaiting')
_____________
Code for TallyGenerator
Viewing 7 posts - 1 through 6 (of 6 total)
You must be logged in to reply to this topic. Login to reply