Berechnen Sie den Zeilenwert basierend auf vorherigen und tatsächlichen Zeilenwerten


9

Hallo allerseits und vielen Dank für Ihre Hilfe.
Ich habe die folgende Situation: eine Tabelle namens Anweisungen, die die Felder ID (int), stmnt_date (Datum), Lastschrift (doppelt), Gutschrift (doppelt) und Saldo (doppelt) enthält. Struktur meines Tisches

Ich möchte den Saldo nach folgenden Regeln berechnen:

Der Saldo der ersten Zeile ( chronologisch ) = Lastschrift - Gutschrift und für den Rest der Zeilen

aktueller Zeilensaldo = chronologisch vorheriger Zeilensaldo + aktuelle Zeilenbelastung - aktuelles Zeilenguthaben

Wie Sie auf dem Bild oben sehen können, sind die Zeilen nicht nach Datum geordnet. Deshalb habe ich das Wort zweimal chronologisch verwendet, um die Bedeutung des Werts stmnt_date hervorzuheben.

Vielen Dank für Ihre Hilfe.


Können Sie die Debit- und Kreditfelder zu einem Feld zusammenfassen? Wenn ja, können Sie negative Werte als Lastschrift und positive Werte als Gutschrift verwenden.
Mike

1
Für zukünftige Fragen (da dies beantwortet wurde) geben Sie die Postleitzahl in Textform und nicht in Druckbildschirmen ein. Fügen Sie auch die CREATE TABLEAnweisungen und Beispieldaten (mit INSERT) hinzu.
Ypercubeᵀᴹ

Als Hommage an Ihre Antwort @ypercube habe ich für jeden, der dies liest, ein Beispiel für CREATE TABLE und INSERT unter dba.stackexchange.com/a/183207/131900
Zack Morris,

Antworten:


8

Unter der Annahme, dass dies stmnt_dateeine UNIQUEEinschränkung hat, wäre dies mit Fenster- / Analysefunktionen ziemlich einfach:

SELECT 
    s.stmnt_date, s.debit, s.credit,
    SUM(s.debit - s.credit) OVER (ORDER BY s.stmnt_date
                                  ROWS BETWEEN UNBOUNDED PRECEDING
                                           AND CURRENT ROW)
        AS balance
FROM
    statements AS s
ORDER BY
    stmnt_date ;

Leider hat MySQL (noch) keine Analysefunktionen implementiert. Sie können das Problem entweder mit striktem SQL lösen, indem Sie die Tabelle selbst verbinden (was ziemlich ineffizient sein sollte, obwohl es zu 100% funktioniert) oder indem Sie eine bestimmte MySQL-Funktion verwenden, Variablen (die recht effizient wären, aber Sie müssten sie testen Stellen Sie beim Upgrade von MySQL sicher, dass die Ergebnisse immer noch korrekt sind und nicht durch Optimierungsverbesserungen beeinträchtigt werden.

SELECT 
    s.stmnt_date, s.debit, s.credit,
    @b := @b + s.debit - s.credit AS balance
FROM
    (SELECT @b := 0.0) AS dummy 
  CROSS JOIN
    statements AS s
ORDER BY
    stmnt_date ;

Mit Ihren Daten ergibt sich:

+------------+-------+--------+---------+
| stmnt_date | debit | credit | balance |
+------------+-------+--------+---------+
| 2014-05-15 |  3000 |      0 |    3000 |
| 2014-06-17 | 20000 |      0 |   23000 |
| 2014-07-16 |     0 |   3000 |   20000 |
| 2014-08-14 |     0 |   3000 |   17000 |
| 2015-02-01 |  3000 |      0 |   20000 |
+------------+-------+--------+---------+
5 rows in set (0.00 sec)

6

Ich denke, Sie könnten Folgendes versuchen:

set @balance := 0;

SELECT stmnt_date, debit, credit, (@balance := @balance + (debit - credit)) as Balance
FROM statements
ORDER BY stmnt_date;

2

Die Antwort von ypercube ist ziemlich spektakulär (ich hatte noch nie eine Variablenerstellung innerhalb einer einzelnen Abfrage über eine solche Dummy-Auswahl gesehen). Hier ist also die Anweisung CREATE TABLE.

Für tabellarische Datenbilder in der Google Bildsuche können Sie https://convertio.co/ocr/ oder https://ocr.space/ verwenden , um sie in ein Textdokument zu konvertieren. Wenn die OCR die Spalten nicht richtig erkannt hat und Sie einen Mac haben, verwenden Sie TextWrangler mit gedrückter Optionstaste, um eine rechteckige Auswahl durchzuführen und die Spalten zu verschieben. Die Kombination aus SQL-Editor wie Sequel Pro , TextWrangler und einer Tabelle wie Google Text & Tabellen macht den Umgang mit tabulatorgetrennten Tabellendaten äußerst effizient.

Wenn ich all dies in einen Kommentar einfügen könnte, würde ich diese Antwort bitte nicht positiv bewerten.

-- DROP TABLE statements;

CREATE TABLE IF NOT EXISTS statements (
  id integer NOT NULL AUTO_INCREMENT,
  stmnt_date date,
  debit integer not null default 0,
  credit integer not null default 0,
  PRIMARY KEY (id)
);

INSERT INTO statements
(stmnt_date  , debit, credit) VALUES
('2014-06-17', 20000, 0     ),
('2014-08-14', 0    , 3000  ),
('2014-07-16', 0    , 3000  ),
('2015-02-01', 3000 , 0     ),
('2014-05-15', 3000 , 0     );

-- this is slightly modified from ypercube's (@b := 0 vs @b := 0.0)
SELECT 
    s.stmnt_date, s.debit, s.credit,
    @b := @b + s.debit - s.credit AS balance
FROM
    (SELECT @b := 0) AS dummy 
CROSS JOIN
    statements AS s
ORDER BY
    stmnt_date ASC;

/* result
+------------+-------+--------+---------+
| stmnt_date | debit | credit | balance |
+------------+-------+--------+---------+
| 2014-05-15 |  3000 |      0 |    3000 |
| 2014-06-17 | 20000 |      0 |   23000 |
| 2014-07-16 |     0 |   3000 |   20000 |
| 2014-08-14 |     0 |   3000 |   17000 |
| 2015-02-01 |  3000 |      0 |   20000 |
+------------+-------+--------+---------+
5 rows in set (0.00 sec)
*/

1

Selbstverbindende Tische sind bei großen Tischen nicht sehr schnell. Als ich mich mit dieser Aufgabe unter PostgreSQL befasste, entschied ich mich, die Triggerfunktion zur Berechnung des gespeicherten Feldes "balance" zu verwenden. Alle Berechnungen werden nur einmal für jede Zeile durchgeführt.

DROP TABLE IF EXISTS statements;

CREATE TABLE IF NOT EXISTS statements (
  id BIGSERIAL,
  stmnt_date TIMESTAMP,
  debit NUMERIC(18,2) not null default 0,
  credit NUMERIC(18,2) not null default 0,
  balance NUMERIC(18,2)
);

CREATE OR REPLACE FUNCTION public.tr_fn_statements_balance()
RETURNS trigger AS
$BODY$
BEGIN

    UPDATE statements SET
    balance=(SELECT SUM(a.debit)-SUM(a.credit) FROM statements a WHERE a.stmnt_date<=statements.stmnt_date)
    WHERE stmnt_date>=NEW.stmnt_date;

RETURN NULL;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

CREATE TRIGGER tr_statements_after_update
  AFTER INSERT OR UPDATE OF debit, credit
  ON public.statements
  FOR EACH ROW
  EXECUTE PROCEDURE public.tr_fn_statements_balance();


INSERT INTO statements
(stmnt_date  , debit, credit) VALUES
('2014-06-17', 20000, 0     ),
('2014-08-14', 0    , 3000  ),
('2014-07-16', 0    , 3000  ),
('2015-02-01', 3000 , 0     ),
('2014-05-15', 3000 , 0     );


select * from statements order by stmnt_date;

-1

In MSSQL zum Beispiel:

Verwenden Sie eine with () -Anweisung, um einen CTE zu generieren. Dies ist im Wesentlichen eine temporäre Ergebnismenge, die den Wert jeder Zeile anzeigt. Sie können math in der with-Anweisung verwenden, um am Ende eine Spalte zu erstellen. Mit math können Sie anzeigen, dass die Gesamtsumme der Zeile DEBIT-CREDIT ist. In Ihrer with-Anweisung müssen Sie jeder Zeile Zeilennummern zuweisen. Verwenden Sie die OVER-Klausel von WITH (), um nach stmnt_date zu sortieren.

Verbinden Sie dann die Tabelle rekursiv mit a.ROWNUMBER = b.ROWNUMBER-1 oder +1 auf sich selbst, wodurch Sie auf a.total + b.total = total dieser Zeile und der vorherigen Zeile verweisen können.

Ich schätze, dass ich den Code nicht zur Verfügung stelle, aber dies ist die praktische Methode, um dies zu erreichen. Ich kann auf Anfrage Code angeben :)


1
Die Frage betrifft MySQL. Es ist zwar nicht schlecht (im Gegenteil), Code dafür bereitzustellen, wie dies mit CTEs oder Fensterfunktionen in DBMS geschehen kann (wie Postgres, SQL-Server, DB2, Oracle, ... die Liste ist lang), aber Sie sollte zumindest Code dazu bereitstellen, wie dies in MySQL zu tun ist.
Ypercubeᵀᴹ
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.