Trigger sind wahrscheinlich wollen Sie wollen. Es wird jedoch hässlich sein, dies richtig und effizient zum Laufen zu bringen. Es ist wahrscheinlich besser, den Saldo nicht in jeder Zeile zu speichern, wenn Sie allzu häufig Zeilen zu früheren Daten einfügen. Verwenden Sie stattdessen Abfragen oder Ansichten das Gleichgewicht zu finden. Um das Guthaben an einem bestimmten Datum zu finden, verbinden Sie es mit den Zeilen für frühere Daten und summieren Sie die Nettoeinlage, gruppiert nach der aktuellen Transaktions-ID:
CREATE VIEW pettybalance
AS SELECT SUM(older.pc_in - older.pc_out) AS balance,
current.pc_id AS pc_id, -- foreign key
current.pc_date AS `date`
FROM pettycash AS current
JOIN pettycash AS older
ON current.pc_date > older.pc_date
OR (current.pc_date = older.pc_date AND current.pc_id >= older.pc_id)
GROUP BY current.pc_id
;
Ich schränke auch older.pc_id
ein kleiner sein als current.pc_id
um eine Unklarheit in Bezug auf das Schema und die Saldoberechnung zu beheben. Seit dem pc_date
nicht eindeutig ist, könnten Sie mehrere Transaktionen für ein bestimmtes Datum haben. Wenn dies der Fall ist, wie hoch sollte der Saldo für jede Transaktion sein? Hier gehen wir davon aus, dass eine Transaktion mit einer größeren ID nach einer Transaktion mit einer kleineren ID kommt, die aber das gleiche Datum hat. Formaler verwenden wir die Reihenfolge
Beachten Sie, dass wir in der Ansicht eine ≥-Reihenfolge basierend auf>:
verwenden
Nachdem ich versucht habe, Trigger richtig zum Laufen zu bringen, empfehle ich, es gar nicht erst zu versuchen. Aufgrund interner Tabellen- oder Zeilensperren beim Einfügen/Aktualisieren müssen Sie die Kontostandsspalte in eine neue Tabelle verschieben, was jedoch nicht allzu umständlich ist (pettycash
umbenennen zu pettytransactions
, erstellen Sie einen neuen pettybalance (balance, pc_id)
Tabelle und erstellen Sie eine Ansicht namens pettycash
als sich pettytransactions
anschließt und pettybalance
auf pc_id
). Das Hauptproblem besteht darin, dass Triggerkörper einmal für jede erstellte oder aktualisierte Zeile ausgeführt werden, was dazu führt, dass sie unglaublich ineffizient sind. Eine Alternative wäre, eine gespeicherte Prozedur
zu erstellen zum Aktualisieren von Spalten, die Sie nach dem Einfügen oder Aktualisieren aufrufen können. Eine Prozedur ist beim Abrufen von Salden leistungsfähiger als eine Ansicht, aber spröder, da Programmierer die Salden aktualisieren müssen, anstatt dies der Datenbank überlassen zu müssen. Die Verwendung einer Ansicht ist das sauberere Design.
DROP PROCEDURE IF EXISTS update_balance;
delimiter ;;
CREATE PROCEDURE update_balance (since DATETIME)
BEGIN
DECLARE sincebal DECIMAL(10,2);
SET sincebal = (
SELECT pc_bal
FROM pettycash AS pc
WHERE pc.pc_date < since
ORDER BY pc.pc_date DESC, pc.pc_id DESC LIMIT 1
);
IF ISNULL(sincebal) THEN
SET sincebal=0.0;
END IF;
UPDATE pettycash AS pc
SET pc_bal=(
SELECT sincebal+SUM(net)
FROM (
SELECT pc_id, pc_in - pc_out AS net, pc_date
FROM pettycash
WHERE since <= pc_date
) AS older
WHERE pc.pc_date > older.pc_date
OR (pc.pc_date = older.pc_date
AND pc.pc_id >= older.pc_id)
) WHERE pc.pc_date >= since;
END;;
delimiter ;
Off-topic
Ein Problem mit dem aktuellen Schema ist die Verwendung von Float
s zum Speichern von Geldwerten. Aufgrund der Art und Weise, wie Gleitkommazahlen dargestellt werden, sind Zahlen, die zur Basis 10 genau sind (d. h. keine sich wiederholende Dezimaldarstellung haben), nicht immer genau als Gleitkommazahlen. Zum Beispiel ist 0,01 (in Basis 10) näher an 0,009999999776482582... oder 0,0100000000000000002081668..., wenn es gespeichert wird. Es ist ungefähr so, als wäre 1/3 zur Basis 3 "0,1", aber 0,333333 ... zur Basis 10. Anstelle von Float
, sollten Sie den Decimal
Typ:
ALTER TABLE pettycash MODIFY pc_in DECIMAL(10,2);
ALTER TABLE pettycash MODIFY pc_out DECIMAL(10,2);
Wenn Sie eine Ansicht verwenden, legen Sie pettycash.pc_bal
ab . Bei Verwendung einer gespeicherten Prozedur zum Aktualisieren von pettycash.pc_bal
, sollte auch geändert werden.