Oracle - Auslöser zum Erstellen einer Protokollzeile beim Aktualisieren

8

Erstens haben wir derzeit das gewünschte Verhalten, aber es ist nicht einfach zu warten, wenn Änderungen an der Datenbank erforderlich sind. Ich suche nach etwas einfacher, effizienter oder einfacher zu warten (alles, was einer dieser 3 wäre sehr willkommen). Wenn wir ein Update durchführen, wird eine Protokollzeile erstellt, die eine Kopie der aktuellen Zeile ist, und die Werte der aktuellen Zeile werden aktualisiert. Das Ergebnis ist, dass wir einen Verlaufsbericht darüber haben, wie die Zeile war, bevor sie aktualisiert wurde.

Begründung: Wir müssen eine Reihe von föderalen Regeln einhalten und haben diesen Weg gewählt, um eine vollständige Audit-Historie von allem zu haben, und wir können die Datenbank zu jedem Zeitpunkt ansehen und sehen, wie die Dinge aussahen ( zukünftige Anforderung). Aus ähnlichen Gründen kann ich nicht ändern, wie der Verlauf aufgezeichnet wird ... jede Lösung muss dieselben Daten enthalten wie die aktuellen Auslöser.

Hier sehen Sie, wie die aktuellen Trigger für die Tabelle Contact aussehen:
(die Felder werden nicht gekürzt, die Anzahl der Felder spielt keine Rolle)

Vor dem Update (in jeder Zeile):

%Vor%

Vor dem Update (einmal für alle Zeilen):

%Vor%

Nach dem Update (einmal für alle Zeilen):

%Vor%

Das Paket definiert als (getrimmt, Vollversion ist nur eine Kopie davon pro Tabelle):

%Vor%

Das aktuelle Ergebnis

Hier ist ein Ergebnis der Geschichte:

%Vor%

Jeder Verlaufseintrag hat eine Entity_ID, die die ID der aktuellen Zeile ist, der Date_Start des neuen Datensatzes entspricht dem Date_Modified der letzten History-Zeile. Dies ermöglicht uns Abfragen wie Where Entity_ID = :id Or ID = :id And :myDate < Date_Modified And :myDate >= Date_Start . Der Verlauf kann von Entity_ID = :current_id abgerufen werden.

Gibt es einen besseren Ansatz, der hoffentlich wartungsfreundlicher / flexibler ist? Das Konzept ist einfach: Wenn Sie eine Zeile aktualisieren, kopieren Sie sie in die gleiche Tabelle über eine Einfügung mit den alten Werten aktualisiere die aktuelle Zeile ... aber tatsächlich mache ich noch einen einfacheren Weg. Ich hoffe, dass jemand, der in Oracle viel kniffliger / weiser ist, einen besseren Ansatz dafür hat. Geschwindigkeit spielt keine große Rolle, wir lesen zu 99% 1% schreibt wie die meisten Webanwendungen, und alle Massenoperationen sind Einfügungen, keine Aktualisierungen, die keinen Verlauf erzeugen würden.

Wenn jemand irgendwelche Ideen hat, um die Wartung zu vereinfachen, wäre ich sehr dankbar, danke!

    
Nick Craver 10.02.2010, 16:57
quelle

7 Antworten

4

Okay, das ist ein Neuschreiben. Was ich bei der ersten Antwort verpasste, ist, dass die Anwendung ihren Verlauf in der Haupttabelle speichert. Jetzt verstehe ich, warum @NickCraver so entschuldigend gegenüber dem Code ist.

Nun, das erste, was zu tun ist, die Täter dieses Designs zu jagen und sicherzustellen, dass sie es nie wieder tun. Das Speichern einer Historie wie dieser skaliert nicht, macht normale (nicht-historische) Abfragen komplizierter und sabotiert die relationale Integrität. Offensichtlich gibt es Szenarien, in denen nichts davon zählt, und vielleicht ist Ihre Website eine davon, aber im Allgemeinen ist dies eine sehr schlechte Implementierung.

Der beste Weg dazu ist Oracle 11g Total Recall . Es ist eine elegante Lösung mit einer völlig unsichtbaren und effizienten Implementierung und - im Vergleich zu den anderen kostenpflichtigen Extras von Oracle - recht preiswert.

Aber wenn Total Recall nicht in Frage kommt und Sie es wirklich tun müssen, erlauben Sie keine Updates . Eine Änderung an einem bestehenden CONTACT-Datensatz sollte eine Einfügung sein. Um dies zu ermöglichen, müssen Sie möglicherweise eine Ansicht mit einem INSTEAD OF-Trigger erstellen. Es ist immer noch eklig, aber nicht ganz so eklig wie das, was du jetzt hast.

Ab Oracle 11.2.0.4 wurde Total Recall in Flashback Archive umbenannt und ist als Teil der Enterprise-Lizenz enthalten (allerdings ohne die komprimierten Journaltabellen, es sei denn, wir kaufen die erweiterte Komprimierungsoption).

Diese Großzügigkeit von Oracle sollte FDA zum normalen Weg machen, Geschichte zu speichern: Es ist effizient, es ist performativ, es ist ein Oracle mit eingebauter Standard-Syntax, um historische Abfragen zu unterstützen. Leider erwarte ich schon seit vielen Jahren halbgekochte Implementierungen mit Triggerfehlern, gebrochenen Primärschlüsseln und schrecklicher Leistung. Denn Journaling scheint eine jener Ablenkungen zu sein, an denen sich die Entwickler erfreuen, trotz der Tatsache, dass es sich um Low-Level-Installationen handelt, die für 99,99% aller Geschäftsvorgänge weitgehend irrelevant sind.

    
APC 10.02.2010, 17:14
quelle
4

Leider gibt es keine Möglichkeit, den Verweis auf alle Spaltennamen (: OLD.this,: OLD.that usw.) in Triggern zu vermeiden. Sie könnten jedoch ein Programm schreiben, um den Triggercode aus der Tabellendefinition (in USER_TAB_COLS) zu generieren . Dann, wann immer die Tabelle geändert wird, können Sie eine neue Kopie der Auslöser erzeugen und zusammenstellen.

Siehe diesen AskTom-Thread für wie um das zu tun.

    
Tony Andrews 10.02.2010 17:05
quelle
4

Falls jemand den gleichen hochspezialisierten Fall hat, den wir machen (Linq access macht die Geschichte einzelner Tabellen viel einfacher / sauberer, das ist es, was ich gemacht habe, um das, was wir haben, zu vereinfachen, willkommen irgendwelche Verbesserungen .... das ist nur ein Skript, das immer dann ausgeführt wird, wenn sich die Datenbank ändert, wobei die Audit-Trigger neu generiert werden, wobei die Hauptänderung PRAGMA AUTONOMOUS_TRANSACTION; ist, die den Verlauf für eine autonome Transaktion erstellt und sich nicht um Mutationen kümmert (was für unsere Prüfung keine Rolle spielt):

%Vor%

Wenn Sie sich verbessern können, fühlen Sie sich frei ... Ich habe nur eine Handvoll PL / SQL-Skripte geschrieben, das Bedürfnis taucht nicht oft auf ... wahrscheinlich ist dort noch viel zu wünschen übrig.

Beantworten Sie APC , um mich dazu zu bringen, mir das etwas genauer anzusehen. Ich empfehle dieses History-Layout nicht, es sei denn, es ist der Rest Ihres Modells / Anwendung / stack extrem gut . Für diese Anwendung zeigen wir ständig eine Mischung aus Historie und Aktualität, und Filterung ist viel einfacher als die Kombination, wenn es um einen Linq-zu-SQL-Stil-Zugriff geht. Danke für all die Antworten Jungs, alle guten Vorschläge ... und wenn ich mehr Zeit habe und nicht von einem Release-Zeitplan geknirscht bin, werde ich das nochmal überprüfen, ob es noch verbessert werden kann.

    
Nick Craver 11.02.2010 01:20
quelle
2

Ich verstehe Ihre spezifischen Anwendungsanforderungen, die Historie und die aktuellen Werte in der gleichen Tabelle zu haben, aber vielleicht könnte dies dadurch geschehen, dass Sie die übliche Route einer separaten Prüftabelle verwenden, die jedoch als pseudo-materialisierte Ansicht aufgebaut wird um eine kombinierte Ansicht für die Anwendung zu präsentieren.

Für mich hat das den Vorteil, eine einfache "aktuelle" Ansicht und eine separate, aber vollständig automatisierte "Audit" -Ansicht zu haben (die in diesem Fall auch die aktuelle Ansicht hat).

Etwas wie:

%Vor%     
Nick Pierpoint 11.02.2010 15:03
quelle
1
___ qstnhdr ___ Oracle - Auslöser zum Erstellen einer Protokollzeile beim Aktualisieren ___ answer2238755 ___

Leider gibt es keine Möglichkeit, den Verweis auf alle Spaltennamen (: OLD.this,: OLD.that usw.) in Triggern zu vermeiden. Sie könnten jedoch ein Programm schreiben, um den Triggercode aus der Tabellendefinition (in USER_TAB_COLS) zu generieren . Dann, wann immer die Tabelle geändert wird, können Sie eine neue Kopie der Auslöser erzeugen und zusammenstellen.

Siehe diesen AskTom-Thread für wie um das zu tun.

    
___ answer2245296 ___

Ich verstehe Ihre spezifischen Anwendungsanforderungen, die Historie und die aktuellen Werte in der gleichen Tabelle zu haben, aber vielleicht könnte dies dadurch geschehen, dass Sie die übliche Route einer separaten Prüftabelle verwenden, die jedoch als pseudo-materialisierte Ansicht aufgebaut wird um eine kombinierte Ansicht für die Anwendung zu präsentieren.

Für mich hat das den Vorteil, eine einfache "aktuelle" Ansicht und eine separate, aber vollständig automatisierte "Audit" -Ansicht zu haben (die in diesem Fall auch die aktuelle Ansicht hat).

Etwas wie:

%Vor%     
___ answer2238811 ___

Okay, das ist ein Neuschreiben. Was ich bei der ersten Antwort verpasste, ist, dass die Anwendung ihren Verlauf in der Haupttabelle speichert. Jetzt verstehe ich, warum @NickCraver so entschuldigend gegenüber dem Code ist.

Nun, das erste, was zu tun ist, die Täter dieses Designs zu jagen und sicherzustellen, dass sie es nie wieder tun. Das Speichern einer Historie wie dieser skaliert nicht, macht normale (nicht-historische) Abfragen komplizierter und sabotiert die relationale Integrität. Offensichtlich gibt es Szenarien, in denen nichts davon zählt, und vielleicht ist Ihre Website eine davon, aber im Allgemeinen ist dies eine sehr schlechte Implementierung.

Der beste Weg dazu ist Oracle 11g Total Recall . Es ist eine elegante Lösung mit einer völlig unsichtbaren und effizienten Implementierung und - im Vergleich zu den anderen kostenpflichtigen Extras von Oracle - recht preiswert.

Aber wenn Total Recall nicht in Frage kommt und Sie es wirklich tun müssen, erlauben Sie keine Updates . Eine Änderung an einem bestehenden CONTACT-Datensatz sollte eine Einfügung sein. Um dies zu ermöglichen, müssen Sie möglicherweise eine Ansicht mit einem INSTEAD OF-Trigger erstellen. Es ist immer noch eklig, aber nicht ganz so eklig wie das, was du jetzt hast.

Ab Oracle 11.2.0.4 wurde Total Recall in Flashback Archive umbenannt und ist als Teil der Enterprise-Lizenz enthalten (allerdings ohne die komprimierten Journaltabellen, es sei denn, wir kaufen die erweiterte Komprimierungsoption).

Diese Großzügigkeit von Oracle sollte FDA zum normalen Weg machen, Geschichte zu speichern: Es ist effizient, es ist performativ, es ist ein Oracle mit eingebauter Standard-Syntax, um historische Abfragen zu unterstützen. Leider erwarte ich schon seit vielen Jahren halbgekochte Implementierungen mit Triggerfehlern, gebrochenen Primärschlüsseln und schrecklicher Leistung. Denn Journaling scheint eine jener Ablenkungen zu sein, an denen sich die Entwickler erfreuen, trotz der Tatsache, dass es sich um Low-Level-Installationen handelt, die für 99,99% aller Geschäftsvorgänge weitgehend irrelevant sind.

    
___ answer2241606 ___

Falls jemand den gleichen hochspezialisierten Fall hat, den wir machen (Linq access macht die Geschichte einzelner Tabellen viel einfacher / sauberer, das ist es, was ich gemacht habe, um das, was wir haben, zu vereinfachen, willkommen irgendwelche Verbesserungen .... das ist nur ein Skript, das immer dann ausgeführt wird, wenn sich die Datenbank ändert, wobei die Audit-Trigger neu generiert werden, wobei die Hauptänderung %code% ist, die den Verlauf für eine autonome Transaktion erstellt und sich nicht um Mutationen kümmert (was für unsere Prüfung keine Rolle spielt):

%Vor%

Wenn Sie sich verbessern können, fühlen Sie sich frei ... Ich habe nur eine Handvoll PL / SQL-Skripte geschrieben, das Bedürfnis taucht nicht oft auf ... wahrscheinlich ist dort noch viel zu wünschen übrig.

Beantworten Sie APC , um mich dazu zu bringen, mir das etwas genauer anzusehen. Ich empfehle dieses History-Layout nicht, es sei denn, es ist der Rest Ihres Modells / Anwendung / stack extrem gut . Für diese Anwendung zeigen wir ständig eine Mischung aus Historie und Aktualität, und Filterung ist viel einfacher als die Kombination, wenn es um einen Linq-zu-SQL-Stil-Zugriff geht. Danke für all die Antworten Jungs, alle guten Vorschläge ... und wenn ich mehr Zeit habe und nicht von einem Release-Zeitplan geknirscht bin, werde ich das nochmal überprüfen, ob es noch verbessert werden kann.

    
___ antwort2238982 ___

Wenn Sie eine generische Lösung entwickeln möchten, sollten Sie sich das DBMS_SQL-Paket ansehen. Damit könnten Sie ein Paket / eine Prozedur entwickeln, die einen Tabellennamen als Eingabe verwendet und die darauf basierenden Aktualisierungen erstellt, indem Sie die Tabellenstruktur im Wörterbuch untersuchen und die Aktualisierungen im laufenden Betrieb erstellen. Es wäre eine nicht-triviale Vorausentwicklung, aber viel weniger Wartung in der Zukunft, da sich der Code, wenn sich eine Tabellenstruktur ändert, dies erfassen und anpassen würde. Diese Methode würde für jede Tabelle funktionieren, mit der Sie sie verwenden möchten.

    
___ tag123oracle ___ Oracle Database ist ein Datenbankmanagementsystem mit mehreren Modellen, das von Oracle Corporation erstellt wurde. Verwenden Sie dieses Tag NICHT für andere Produkte von Oracle wie Java und MySQL. ___ answer2239271 ___

Abhängig von der Komplexität Ihrer Datenbank (Anzahl der Tabellen, Größe, Tiefe der PK / FK-Beziehungen, andere Logik in Triggern) sollten Sie sich Oracle Workspace Management . Sie führen einen API-Aufruf aus, um eine Tabelle unter Workspace Management zu platzieren, was dazu führt, dass Oracle die Tabelle durch eine aktualisierbare Ansicht und andere entsprechende Objekte ersetzt, die einen Verlauf aller Versionen der Zeilen beibehalten.

Ich habe das verwendet, und obwohl es Nachteile gibt, besteht ein Vorteil für das Auditing darin, dass die Codeobjekte alle von Oracle generiert werden und ihre Korrektheit allgemein angenommen wird.

    
___ tag123triggers ___ Trigger sind Regeln, die, wenn sie als wahr ausgewertet werden, eine oder mehrere Aktionen ausführen. ___ answer2241496 ___

Die einzige Zeit, zu der ich empfehle, dass historische Aufzeichnungen in der gleichen Tabelle wie die "aktuellen" Aufzeichnungen gespeichert werden, ist, wenn FK Links zu den Aufzeichnungen mit ihnen verlinkt werden müssen oder müssen. Zum Beispiel hatte eine Anwendung, die ich gesehen habe, einige FK-Links, die zu einem "Zeitpunkt" mit dem Datensatz verknüpft waren, dh wenn der Datensatz aktualisiert wurde, würde der FK immer noch mit dem historischen Datensatz verknüpfen - das war ein Ein wichtiger Teil des Designs und die Trennung von historischen Aufzeichnungen in eine zweite Tabelle hätte es unhandlicher gemacht.

Abgesehen davon würde ich es vorziehen, dass eine geschäftliche Anforderung zum Verfolgen aller Änderungen mit einer separaten Tabelle "Verlauf" für jede Tabelle gelöst werden sollte. Sicher, es bedeutet mehr DDL, aber es vereinfacht den Anwendungscode enorm und Sie profitieren auch von einer besseren Leistung und Skalierbarkeit.

    
___ tag123audit ___ Eine Reihe von Prozessen oder Funktionen, die Änderungen an einer oder mehreren Komponenten eines Systems verfolgen und die Vollständigkeit und Genauigkeit der Transaktionsverarbeitung, Autorisierung und Gültigkeit von Systemoperationen sicherstellen. ___ qstntxt ___

Erstens haben wir derzeit das gewünschte Verhalten, aber es ist nicht einfach zu warten, wenn Änderungen an der Datenbank erforderlich sind. Ich suche nach etwas einfacher, effizienter oder einfacher zu warten (alles, was einer dieser 3 wäre sehr willkommen). Wenn wir ein Update durchführen, wird eine Protokollzeile erstellt, die eine Kopie der aktuellen Zeile ist, und die Werte der aktuellen Zeile werden aktualisiert. Das Ergebnis ist, dass wir einen Verlaufsbericht darüber haben, wie die Zeile war, bevor sie aktualisiert wurde.

Begründung: Wir müssen eine Reihe von föderalen Regeln einhalten und haben diesen Weg gewählt, um eine vollständige Audit-Historie von allem zu haben, und wir können die Datenbank zu jedem Zeitpunkt ansehen und sehen, wie die Dinge aussahen ( zukünftige Anforderung). Aus ähnlichen Gründen kann ich nicht ändern, wie der Verlauf aufgezeichnet wird ... jede Lösung muss dieselben Daten enthalten wie die aktuellen Auslöser.

Hier sehen Sie, wie die aktuellen Trigger für die Tabelle %code% aussehen:
(die Felder werden nicht gekürzt, die Anzahl der Felder spielt keine Rolle)

Vor dem Update (in jeder Zeile):

%Vor%

Vor dem Update (einmal für alle Zeilen):

%Vor%

Nach dem Update (einmal für alle Zeilen):

%Vor%

Das Paket definiert als (getrimmt, Vollversion ist nur eine Kopie davon pro Tabelle):

%Vor%

Das aktuelle Ergebnis

Hier ist ein Ergebnis der Geschichte:

%Vor%

Jeder Verlaufseintrag hat eine Entity_ID, die die ID der aktuellen Zeile ist, der Date_Start des neuen Datensatzes entspricht dem Date_Modified der letzten History-Zeile. Dies ermöglicht uns Abfragen wie %code% . Der Verlauf kann von %code% abgerufen werden.

Gibt es einen besseren Ansatz, der hoffentlich wartungsfreundlicher / flexibler ist? Das Konzept ist einfach: Wenn Sie eine Zeile aktualisieren, kopieren Sie sie in die gleiche Tabelle über eine Einfügung mit den alten Werten aktualisiere die aktuelle Zeile ... aber tatsächlich mache ich noch einen einfacheren Weg. Ich hoffe, dass jemand, der in Oracle viel kniffliger / weiser ist, einen besseren Ansatz dafür hat. Geschwindigkeit spielt keine große Rolle, wir lesen zu 99% 1% schreibt wie die meisten Webanwendungen, und alle Massenoperationen sind Einfügungen, keine Aktualisierungen, die keinen Verlauf erzeugen würden.

Wenn jemand irgendwelche Ideen hat, um die Wartung zu vereinfachen, wäre ich sehr dankbar, danke!

    
___
DCookie 10.02.2010 17:33
quelle
1

Abhängig von der Komplexität Ihrer Datenbank (Anzahl der Tabellen, Größe, Tiefe der PK / FK-Beziehungen, andere Logik in Triggern) sollten Sie sich Oracle Workspace Management . Sie führen einen API-Aufruf aus, um eine Tabelle unter Workspace Management zu platzieren, was dazu führt, dass Oracle die Tabelle durch eine aktualisierbare Ansicht und andere entsprechende Objekte ersetzt, die einen Verlauf aller Versionen der Zeilen beibehalten.

Ich habe das verwendet, und obwohl es Nachteile gibt, besteht ein Vorteil für das Auditing darin, dass die Codeobjekte alle von Oracle generiert werden und ihre Korrektheit allgemein angenommen wird.

    
dpbradley 10.02.2010 18:15
quelle
0

Die einzige Zeit, zu der ich empfehle, dass historische Aufzeichnungen in der gleichen Tabelle wie die "aktuellen" Aufzeichnungen gespeichert werden, ist, wenn FK Links zu den Aufzeichnungen mit ihnen verlinkt werden müssen oder müssen. Zum Beispiel hatte eine Anwendung, die ich gesehen habe, einige FK-Links, die zu einem "Zeitpunkt" mit dem Datensatz verknüpft waren, dh wenn der Datensatz aktualisiert wurde, würde der FK immer noch mit dem historischen Datensatz verknüpfen - das war ein Ein wichtiger Teil des Designs und die Trennung von historischen Aufzeichnungen in eine zweite Tabelle hätte es unhandlicher gemacht.

Abgesehen davon würde ich es vorziehen, dass eine geschäftliche Anforderung zum Verfolgen aller Änderungen mit einer separaten Tabelle "Verlauf" für jede Tabelle gelöst werden sollte. Sicher, es bedeutet mehr DDL, aber es vereinfacht den Anwendungscode enorm und Sie profitieren auch von einer besseren Leistung und Skalierbarkeit.

    
Jeffrey Kemp 11.02.2010 00:48
quelle

Tags und Links