May 27, 2005 at 3:30 pm
CREATE TABLE #Bills
(
BillNo INT identity,
Account INT,
Amount NUMERIC(10,2),
Paid BIT
)
INSERT #Bills (Account, Amount, Paid) Values (45, 200, 0)
INSERT #Bills (Account, Amount, Paid) Values (45, 25, 0)
INSERT #Bills (Account, Amount, Paid) Values (45, 300, 0)
INSERT #Bills (Account, Amount, Paid) Values (45, 50, 0)
INSERT #Bills (Account, Amount, Paid) Values (45, 125, 0)
INSERT #Bills (Account, Amount, Paid) Values (45, 150, 0)
INSERT #Bills (Account, Amount, Paid) Values (50, 200, 0)
INSERT #Bills (Account, Amount, Paid) Values (50, 25, 0)
INSERT #Bills (Account, Amount, Paid) Values (50, 25, 0)
INSERT #Bills (Account, Amount, Paid) Values (50, 50, 0)
INSERT #Bills (Account, Amount, Paid) Values (50, 125, 0)
INSERT #Bills (Account, Amount, Paid) Values (50, 75, 0)
GO
CREATE TABLE #Payments
(
PaymentID INT identity,
Account INT,
Amount NUMERIC(10,2)
)
INSERT #Payments (Account, Amount, Paid) Values (45, 150)
INSERT #Payments (Account, Amount, Paid) Values (50, 100)
GO
My work is to update #Bills table for which bills are paid. For example Account 50 has paid value of 100. So I can update #Bills for a 75 and 25 or 25, 25, 50. I am doing this via loop. The table has about 10000 rows per month and it takes forever. Is there a easy way to do it. Data is coming in text files and uploaded every month.
Regards,
gova
May 27, 2005 at 7:57 pm
A couple of questions. Your example assumes that a payment will always equal a bill or the sum of several bills. What happens when a customer makes a partial payment. BillNo1 is $100 a payment of $75 is recieved Leaving a balance of $25 owed? Are payments applied using FIFO (first in first out) where all payments would be applied to the oldest bill until it was paid in full before payments were applied to other bills? What happens when a customer makes an overpayment. Bill = $100 and customer pays $150?
Mike
May 27, 2005 at 8:40 pm
This query will give you a running total of a customers account
HTH Mike
SELECT b.Account As Account#,
Sum(DISTINCT b.Amount) as "Total Billed",
Sum(DISTINCT P.Amount) AS "Total Payments",
(Sum(DISTINCT b.Amount) - Sum(DISTINCT P.Amount)) AS "Balance"
From
#Bills B
INNER Join
#Payments P
ON
P.Account = B.Account
Group by
B.account
/*
Returns
Account#Total billed Total PD Balance l
-------------------------------------------
45 850.00 150.00 700.00
50 475.00 100.00 375.00
(2 row(s) affected)
*/
May 28, 2005 at 10:40 pm
Thanks Michael.
Only bills will be paid. It is possible to make two or more bills together but not partial payments.
Finding the balance amount and paid amount for an account is easy.
The task is to generate a report stating which are the bills paid. It is allowed to mark any bill(s) which will satisfy the paid amount. (As stated $100 can be for a bill of $100 or for bills $50 + $50 or $25 + $50 + $25)
I am now checking everything in a loop. Checking single values. Then sum(2 rows) then Sum(3rows). It takes forever to complete the process. Possible solutions can be in SQL or C#. Since datafeed loads the table from a text file (BCP from external source) I am not creating a trigger.
This is really a challenging logic to develop.
Regards,
gova
May 29, 2005 at 12:52 pm
Question does the text file contain both new bills, and payments or just payments? What are the business rules for excess amounts since you do not record part payments (do you send these amounts back to the customer)? Would you post the code you are now using.
Thanks
Mike
May 30, 2005 at 4:27 pm
Sorry for delayed replies. Memorial Day Week End
Our application is billing. We generate bills.
We then pass all our bills to an accounting application. Accounting application provides datafeeds each month in a text file. There will not be a partial from a customer. It will be paid for a bill or bills.
Current code we don't have net access in the development machines.
The logic would be
Step1 : If payment.Amount = Any of bill.Amount Update bill.paid = 1 where... 'one bil
Step2 : If payment.Amount = Sum(bill.Amount) Update bill.paid = 1 where... 'all bills
Step3 : If payment.Amount = Sum(any 2 rows of bill.Amount) Update bill.paid = 1 where... '2 bills
Step4 : If payment.Amount = Sum(any 3 rows of bill.Amount) Update bill.paid = 1 where... '3 bills
Goes until no of unpaid bills.
To be honest I can't make a 100% perfect logic after step 2. Any help in the logic would be great. Not an easy task I would say.
Regards,
gova
May 30, 2005 at 11:01 pm
It ate my post. The logic is not to bad you can use a bubble sort to find the values that are equal to a payment. (Assuming that a payment will always be equal to a bill or the sum of one or more bills.) The logic is to order the bills desc (highest to lowest) then for each payment search the list until you find a bill that is smaller or equal to payment. If the bill equals the payment mark it paid and remove it from the array. If not you make successive passes through the list adding the values until you reach one that matches the payment. Somewhere I have the code for a bubble sort that can be modified to meet your needs. Example: Bills {100,90,50,25,25,10} Payment = 110. Since this is larger than the largest value in bills add each value until you have a hit. 100 + 90 > 110 then 100+50 > 110 ...100+10 = 110 a hit mark those two bills paid leaving bills {90,50,25,25} another payment of 100: bubble through 90+50,90+25,90+25, 50+25 (here since 50+25 = 75 less than 100 we know that we will have to add three values so adding the next value 50+25+25 bingo mark thest 3 paid leaving {90} if the next payment is not 90 off goes the nasty gram to accounting.
If you would like I will get you the code for the bubble sort. Unless and I hope they do as this will be very very slow someone else has a better answer.
Mike
May 31, 2005 at 5:43 am
Lost another post and I have to run but
Try this to get rid of exact matches first.
HTH Mike
DECLARE payments_cursor cursor FOR
SELECT *
FROM PAYMENTS P
INNER JOIN Bills b
on b.amount = p.amount
WHERE b.paid = 0 and p.paid = 0
OPEN PAYMENTS_CURSOR
FETCH NEXT FROM PAYMENTS_cursor
-- Check @@FETCH_STATUS to see if there are any more rows to fetch.
WHILE @@FETCH_STATUS = 0
BEGIN
UPDATE BILLS SET PAID = 1
WHERE CURRENT OF PAYMENTS_CURSOR
UPDATE Payments SET Paid = 1
WHERE current of PAYMENTS_CURSOR
-- This is executed as long as the previous fetch succeeds.
FETCH NEXT FROM PAYMENTS_cursor
END
CLOSE PAYMENTS_cursor
DEALLOCATE PAYMENTS_cursor
GO
May 31, 2005 at 7:33 am
Thanks MIKE. I eliminate exact matches and Sum of matches as per code below.
My code is some thing similar to this. I(Any way I convinced accounts to add a Bill field in their payments. Now the ball went to their court(Hope it will not come back). Still if there is an easy solution I will be glad to have it)
SET NOCOUNT ON
GO
DROP TABLE #Bills, #Payments
GO
CREATE TABLE #Bills
(
BillNo INT identity,
Account INT,
Amount NUMERIC(10,2),
Paid BIT
)
INSERT #Bills (Account, Amount, Paid) Values (45, 200, 0)
INSERT #Bills (Account, Amount, Paid) Values (45, 25, 0)
INSERT #Bills (Account, Amount, Paid) Values (45, 300, 0)
INSERT #Bills (Account, Amount, Paid) Values (45, 50, 0)
INSERT #Bills (Account, Amount, Paid) Values (45, 125, 0)
INSERT #Bills (Account, Amount, Paid) Values (45, 150, 0)
INSERT #Bills (Account, Amount, Paid) Values (50, 200, 0)
INSERT #Bills (Account, Amount, Paid) Values (50, 25, 0)
INSERT #Bills (Account, Amount, Paid) Values (50, 25, 0)
INSERT #Bills (Account, Amount, Paid) Values (50, 50, 0)
INSERT #Bills (Account, Amount, Paid) Values (50, 125, 0)
INSERT #Bills (Account, Amount, Paid) Values (50, 75, 0)
INSERT #Bills (Account, Amount, Paid) Values (55, 75, 0)
INSERT #Bills (Account, Amount, Paid) Values (55, 25, 0)
INSERT #Bills (Account, Amount, Paid) Values (55, 35, 0)
INSERT #Bills (Account, Amount, Paid) Values (55, 15, 0)
INSERT #Bills (Account, Amount, Paid) Values (60, 250, 0)
INSERT #Bills (Account, Amount, Paid) Values (60, 250, 0)
INSERT #Bills (Account, Amount, Paid) Values (60, 450, 0)
INSERT #Bills (Account, Amount, Paid) Values (60, 100, 0)
GO
CREATE TABLE #Payments
(
PaymentID INT identity,
Account INT,
Amount NUMERIC(10,2)
)
INSERT #Payments (Account, Amount) Values (45, 150)
INSERT #Payments (Account, Amount) Values (50, 100)
INSERT #Payments (Account, Amount) Values (55, 150)
INSERT #Payments (Account, Amount) Values (60, 250)
INSERT #Payments (Account, Amount) Values (60, 350)
GO
/* UPDATES WHEN SINGLE AMOUNT MATCHES */
UPDATE #Bills
SET
Paid = 1
FROM
(SELECT MAX(BillNo) BillNo, Account, Amount
FROM
#Bills
GROUP BY Account, Amount) Bills
JOIN #Payments
ON #Payments.Account = Bills.Account
WHERE
#Bills.BillNo = Bills.BillNo AND
#Payments.Account = Bills.Account AND
#Payments.Amount = Bills.Amount
/* UPDATES WHEN TOTAL SUM AMOUNT MATCHES */
UPDATE #Bills
SET
Paid = 1
FROM
(SELECT Bills.Account, Bills.Amount
FROM
(SELECT Account, SUM(Amount) Amount
FROM
#Bills
WHERE
Paid = 0
GROUP BY Account) Bills
JOIN #Payments
ON #Payments.Account = Bills.Account AND
#Payments.Amount = Bills.Amount ) Payments
WHERE
Payments.Account = #Bills.Account
/* UPDATES WITH SUM OF TWO ROWS */
DECLARE @account INT
DECLARE @Amount NUMERIC(10,2)
DECLARE @Ctr INT
DECLARE @CurCtr INT
DECLARE Payments_cursor CURSOR FOR
SELECT Account, Amount
FROM
#Payments
OPEN Payments_cursor
FETCH NEXT FROM Payments_cursor INTO @account, @Amount
WHILE @@FETCH_STATUS = 0
BEGIN
IF EXISTS (SELECT * FROM #Bills WHERE Account = @account AND Paid = 0)
BEGIN
PRINT STR(@Account) + ' - ' + STR(@Amount)
/* I am lost here I don't have a perfect logic to update values */
END
FETCH NEXT FROM Payments_cursor INTO @account, @Amount
END
CLOSE Payments_cursor
DEALLOCATE Payments_cursor
Regards,
gova
June 4, 2005 at 5:56 am
Hi the following client side code (vb 6.0) will give you the results you want. Another thought is that since you must use row scans if you have a large number of rows in your bills and payments tables from previous months (paid = 1) this will cause a major hit to performance.
HTH Mike
p.s. If you test this code aganist a copy of actual data would you let me know how it works compared to your current code. Thanks Mike
***********************************
edited to add ALL the code I have got to get this cut and paste thingy under control.
***********************************
Private Sub UpdatePayments(cn As Connection, Account As Integer)
On Error GoTo ErrorHandler
Dim rs As New ADODB.Recordset
Dim SQL As String
SQL = "UPDATE Payments SET Paid = 1" _
& " WHERE Account =" & Account
rs.Open SQL, cn
Exit_sub:
Set rs = Nothing
Exit Sub
ErrorHandler:
MsgBox Err.Number & " " & Err.Description, vbOKOnly, "Error in UpdatePayments"
End Sub
Private Function Pay() As Boolean
On Error GoTo ErrorHandler
Dim cn As New ADODB.Connection
Dim SQL As String
Dim rsPayments As New ADODB.Recordset
Dim rsBills As New ADODB.Recordset
Dim TotalPayments As Currency
Dim TotalBills As Currency
Dim CurrentAccount As Integer
Dim AryBillsPaid() As Integer
Dim count As Integer
Dim x As Integer
rsPayments.CursorType = adOpenKeyset
rsPayments.LockType = adLockOptimistic
rsBills.CursorType = adOpenKeyset
rsBills.LockType = adLockOptimistic
cn.CursorLocation = adUseClient
cn.Provider = "SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=Test;Data Source=MICHAEL-NIFJLS1\mikesdb"
cn.Open
SQL = "SELECT sum(amount) as pAmount, account as PAccount" _
& " FROM Payments" _
& " WHERE Paid = 0" _
& " GROUP BY Account"
rsPayments.Open SQL, cn
rsPayments.MoveFirst
While Not rsPayments.EOF Or rsPayments.BOF
TotalPayments = rsPayments.Fields("PAmount")
CurrentAccount = rsPayments.Fields("PAccount")
SQL = "SELECT max(Amount)AS Amount,BillNo,Paid" _
& " FROM Bills" _
& " WHERE Account =" & CurrentAccount _
& " AND Amount <=" & TotalPayments _
& " AND Paid = 0" _
& " GROUP BY amount,BillNo,Paid"
With rsBills
.Open SQL, cn
.MoveFirst
Do While Not .EOF Or Not .BOF
count = count + 1
ReDim Preserve AryBillsPaid(count)
AryBillsPaid(count) = .Fields("BillNo")
TotalBills = TotalBills + .Fields("Amount")
If TotalBills = TotalPayments Then
For x = 1 To count
If AryBillsPaid(x) <> 0 Then
.MoveFirst
.Find "BillNo =" & AryBillsPaid(x)
.Update "Paid", 1
End If
Next
UpdatePayments cn, rsPayments.Fields("pAccount")
rsBills.Close
Exit Do
End If
If TotalBills > TotalPayments Then
AryBillsPaid(count) = 0
TotalBills = TotalBills - .Fields("Amount")
End If
rsBills.MoveNext
Loop
End With
count = 0
TotalBills = 0
rsPayments.MoveNext
Wend
Exit_sub:
rsPayments.Close
cn.Close
Set rsPayments = Nothing
Set cn = Nothing
Exit Function
ErrorHandler:
MsgBox Err.Number & " " & Err.Description, vbOKOnly, "Error in pay"
End Function
Viewing 10 posts - 1 through 9 (of 9 total)
You must be logged in to reply to this topic. Login to reply