Historie eines Datensatzes speichern

Hallo zusammen;
in unserer 4D Anwendung müssen wir sämtliche Änderungen eines Datensatzes dokumentieren, also wer hat einen Datensatz angelegt, gelöscht, geändert …
Die Historie muss mindestens für 2 Jahre abrufbar sein. In der Historie muss klar erkennbar sein, welcher vorheriger Wert in welchen Wert geändert wurde.
Ich überlege, ob ich Trigger dazu verwende. Innerhalb des Triggers dynamisch die Felder zu prüfen um dann einzelne Datensätze dazu generieren. Leider konnte ich kein Beispiel dazu finden.
Hat jemand das schon mal gemacht oder hat jemand eine Idee.

Vielen Dank

Klaus Vette

Product :4D - 4D Server
4D : v15.4
OS : Windows

ich würde das wie ein Journal führen

Ich würde das nicht im Trigger machen, sondern alle SAVE RECORD in MySave(->[Table]) kapseln, dort das Journal führen und zum Schluß SAVE RECORD ergo Trigger starten.
Meines Wissens funktioniert Old inzwischen mit allen Datentypen also gut generisch zu lösen.

Damit das schnell geht, die zu loggenden Änderungen in ein Object packen und dieses an einen WORKER übergeben.
Reicht die Performance nicht, beim Laden zum Ändern den Datensatz in ein Object packen (würde ich im Datensatz aufheben), vor dem Save den geänderten Datensatz in ein Object packen und beide Objecte dem Journal-Manager übergeben.

Schon gemacht? http://dddd.mettre.de/wp/json-chopping-and-changing/?forumJa, aber nicht für diesen Zweck>.

Ich habe für das Logging ein einfaches Textfeld im Datensatz selbst.
In Ihrem Fall ginge das aber nicht, denn erstens sollen ja auch gelöschte Datensätze geloggt werden, und zweitens kann man Änderungen an Textfeldern schlecht in einem Textfeld dokumentieren.

Deshalb würde ich einfach einen Trigger bauen, der alle Änderungen in eine Log-Tabelle schreibt.
Oder wie vorgeschlagen beim Sichern des Datensatzes direkt aufrufen, aber da muss man höllisch aufpassen, dass man bei späteren Änderungen nichts vergisst. Denn atensätze werden ja nicht nur mit SAVE RECORD gespeichert, sondern z. B. auch durch Klick auf Nächster Datensatz.

Wichtig ist auch, wer später wie an die Daten rankommen soll.

Hallo Herr Zillgen,
guter Ansatz, was meinen Sie mit “record-Version-L”! Der “Save Record”, “Duplicate Record”, “Delete Record”, “Create Record” und viele andere Befehle sind schon seit Jahren gekapselt. Mit dem Old Befehl ließe es sich gut generisch lösen.
Zusätzlich kommt aber hinzu, das z.B. ein Datensatz (z.B. eine Probe mit vielen Verknüpfungen zu anderen Stammdaten-Tabellen) mit einer ensprechenden ID verknüpft sind und ich dem Anwender natürlich nicht anzeigen möchte, dass eine ID von 10 auf 15 geändert wurde, sondern das ein Anwender die Probe von “Pilsener” aus “Export” geändert hat. Dazu wurden sämtliche ID´s schon immer mit “ID_XXXX” (XXXX = Modulkürzel) in der Datenbankstruktur definiert. Also könnte ich bei der Anzeige die ID´s wieder auflösen.
Ansonsten würde ich auf jeden Fall eine eigene Logbuch-Tabelle verwenden, damit vielleicht auch diese Records z.B. über ein Serverprozess zyklisch über einen definierbaren Zeitraum wieder löscht werden, da z.B. nach 2 Jahren es nicht mehr interessiert, wer welche Probe wie geändert hat.
Ich bin mir ansonsten noch unsicher, ob die Performance bei einer Anzeige des Logbuchs ausreicht. Eine Probe könnte aus bis zu 50 verlinkten Datensätzen bestehen, zu den dann die Informationen Neu, geändert … gespeichert sind. In einem Produktions-Bericht enstehen durchaus 100-te Datensätze, die jeweils eine Historie haben. Der Anwedner muss dann aber zu dem Produktions-Bericht sämtliche Änderungen aufgelistet bekommen.

: Klaus VETTE

…was meinen Sie mit “record-Version-L”
bei manchen Anwendung wird mit jedem Sichern ein Longintfeld um 1 erhöht. Das ist die Datensatz-Versionsnummer (L = longint).
Nehmen Sie an der Worker hat viel zu tun, hängt wo anders, dann ist gut zu wissen, welche Versionsnummer der Datensatz hat, an der jetzt knabbert. Noch wichtiger bei Web-Clients oder 4D-Satelliten.

: Ortwin ZILLGEN

ich würde das wie ein Journal führen

Tabellen-Nr
record-PK
record-Version_L
Feld-Nr
alterWert
neuerWert
Current user
Current date
Current time

funktioniert, je nach Anwendung werden in einem Vorgang aber oft mehr als ein Feld geändert. In meinen Tests wurden mit dieser Struktur schnell riesige Datenmengen erreicht.

Besser ist - meiner Meinung nach - statt der Kombination Feld-Nr, alterWert, neuerWert ein Objektfeld, mit einem Eintrag pro geändertem Feld.

Selbst dabei kamen bei mir aber schnell sehr große Datenmengen zusammen, as Änderungsjournal hat bereits nach einem Jahr Laufzeit die Größe der Datendatei selbst erreicht.

Deshalb ist es eine Überlegung eine eigene Struktur aufzusetzen und diese mit einem Duplikat des Servers zu betreiben (auf gleichem Rechner). Diese führt nur das Journal. Will man die History anzeigen, die Daten per REST, Web Service oder SQL Login abholen.

Statt nun für alle Tabellen Trigger anzulegen, lässt sich das auch gut völlig generisch abbilden, der zweite Server verwendet dafür einfach das bereits bestehende Journal und liest dieses per LOG FILE TO JSON:
http://doc.4d.com/4Dv16/4D/16/LOG-FILE-TO-JSON.301-3036817.de.html

Vorteil: die eigentliche Anwendung muss überhaupt nicht verändert werden, es entsteht keine zusätzliche Last, Datendatei bleibt kompakt, Wartung/Backup bleibt schnell und unverändert - die Historie ist autark.