Ist es möglich, eine SQL-Abfrage zu schreiben, die Zahlungen und Gebühren automatisch abgleicht/transaktionalisiert?
-
16-10-2019 - |
Frage
Ich arbeite immer noch an dem hier erwähnten Projekt (http://dba.stackexchange.com/questions/2428/how-do-i-properly-design-a-many-to-many-charges-zahlungs-accounting-system). ).
Das System soll dem Benutzer die Möglichkeit geben, entweder bestimmte Beträge für bestimmte Gebühren zu zahlen oder generische Zahlungen vorzunehmen, bei denen Sie selbst entscheiden können.Angesichts der Tabellenstruktur, die wir gewählt haben (PAYMENTS, CHARGES, PAYMENTS_TO_CHARGES), möchte ich ein wenig schummeln.Im Grunde suche ich nach einer erstaunlich schicken „Abgleichs“-SQL-Abfrage, die Folgendes bewirkt:
SCHRITT 1) Holen Sie sich alle Zahlungen mit Restguthaben (im Wesentlichen Guthaben)
SCHRITT 2) Holen Sie sich alle Gebühren mit Restguthaben (teilweise bezahlt usw.)
SCHRITT 3) Fügen Sie Teile der Zahlungen in die Tabelle PAYMENTS_TO_CHARGES ein, bis kein Guthaben mehr verfügbar ist oder keine Gebühren mehr anfallen.
…Ich denke, technisch gesehen handelt es sich dabei weniger um einen Abgleich als vielmehr um die Erstellung von Transaktionsdaten, aber Sie verstehen schon, worauf es ankommt.
Die Schritte 1 und 2 sind offensichtlich sehr einfach.Der Killer ist Schritt 3.Wenn es keine schicke Möglichkeit gibt, dies in SQL zu tun, werde ich wohl mit der alten handcodierten Schritt-für-Schritt-Schleife durch jede Transaktion gehen und payment_to_charge posten ... ich dachte nur, ich frage mal.
Dank im Voraus!
EDIT 1:Ich habe mir diese Abfrage ausgedacht, um festzustellen, welche Gebühren noch übrig sind, aber sie gibt mir eine Fehlermeldung mit der Meldung „Unbekannte Spalte ‚remaining_balance‘ in ‚where-Klausel‘“:
SELECT
charges.*
, (charges.amount - transactions.total_paid) as remaining_balance
FROM charges
, (SELECT
charge_id
, sum(amount) as total_paid
FROM payments_to_charges
GROUP BY charge_id) as transactions
WHERE charges.member_id = 123
AND charges.id = transactions.charge_id
AND remaining_balance > 0
AND charges.active_on < NOW()
Ich bin mir sicher, dass bestimmte Elemente einfach nicht in Ordnung sind, aber ich kann nicht herausfinden, was bei dieser speziellen Abfrage generell falsch ist.Sollte ich HAVING anstelle von WHERE verwenden?Übersehe ich noch etwas völlig Offensichtliches?
Lösung
Möglicherweise können Sie INSERT in Payemnts_To_Charges in einer SQL-Anweisung ausführen, aber ich bin mir nicht sicher, ob es sich lohnt.Es scheint, dass dies im prozeduralen Code einfacher zu erstellen, zu debuggen und zu warten wäre.Etwas wie das:
CREATE TABLE Payments (PaymentId Number(10), Amount Number(6,2));
CREATE TABLE Charges (ChargeID Number(10), Amount Number(6,2));
CREATE TABLE Payments_To_Charges
(PaymentID Number(10), ChargeID Number(10), Amount Number(6,2));
INSERT INTO Payments VALUES (1,4);
INSERT INTO Payments VALUES (2,4);
INSERT INTO Charges VALUES (1,2);
INSERT INTO Charges VALUES (2,5);
INSERT INTO Charges VALUES (3,6);
INSERT INTO Charges VALUES (4,4);
INSERT INTO Charges VALUES (5,10);
Declare
vPaymentAmount Payments.Amount%Type;
vAppliedAmount Payments_To_Charges.Amount%Type;
Begin
For vPayment In (SELECT PaymentID, Amount FROM Payments) Loop
vPaymentAmount := vPayment.Amount;
For vCharge In (
SELECT ChargeID, Amount FROM
(
SELECT ChargeID, Amount -
NVL((SELECT SUM(Amount) FROM Payments_To_Charges pc
WHERE pc.ChargeID = c.ChargeID),0) Amount
FROM Charges c
) WHERE Amount > 0
) Loop
vAppliedAmount := LEAST(vPaymentAmount, vCharge.Amount);
INSERT INTO Payments_To_Charges (PaymentID, ChargeID, Amount)
VALUES (vPayment.PaymentID, vCharge.ChargeID, vAppliedAmount);
vPaymentAmount := vPaymentAmount - vAppliedAmount;
If (vPaymentAmount = 0) Then
Exit;
End If;
End Loop;
End Loop;
End;
/
SELECT * FROM Payments_To_Charges;
Aktualisieren:
„Remaining_balance“ ist das Problem.Sie können in der WHERE-Klausel nicht auf den Aliaswert verweisen.Sie können die Abfrage entweder in eine Unterabfrage umwandeln und diese Bedingung auf einer höheren Ebene hinzufügen oder den verbleibenden Restbetrag in der WHERE-Klausel für (charges.amount - Transactions.total_paid) ändern.