Mysql
 sql >> Datenbank >  >> RDS >> Mysql

TABELLE MIT SUMME AKTUALISIEREN

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.