2016-07-01 4 views
1

Ich habe einige Tabellen, für die ich Daten versionieren möchte. Soweit es das Design betrifft, sind dies nur Einfügetische. Die Tische sind so etwas wie diesDatenversionierung mit Oracle

TABLENAME 
---------- 
SURROGATE_KEY_ID 
USER_VISIBLE_NATURAL_KEY_ID 
VERSION 
VALUE 

Was ich will, ist für immer, wenn ich ein Datensatz einfügen, sollte der neue Datensatz eine Version, die für einen gegebenen USER_VISIBLE_NATURAL_KEY_ID Satz einer größer als die vorherige max-Version. Meine Wünsche für Version ist es, neue Daten aus alten Daten zu erzählen, während die alten Daten erhalten bleiben. Sie wissen, Standard-Verlaufsverfolgung, Audit etwas Zeug.

ich einen Auslöser wie

CREATE OR REPLACE TRIGGER TABLENAME_TRIGGER 
BEFORE INSERT ON TABLENAME 
REFERENCING NEW AS NEW 
FOR EACH ROW 
DECLARE 
    LATEST VERSION NUMBER; 
BEGIN 
    SELECT NVL(MAX(VERSION), 0) INTO LATEST_VERSION FROM TABLENAME 
    WHERE TABLE_NAME.USER_VISIBLE_NATURAL_KEY_ID = :NEW.USER_VISIBLE_NATURAL_KEY_ID; 
    :NEW.VERSION = LATEST_VERSION +1; 
END; 

versucht, einen mutierenden Tabellenfehler Das gibt mir, nehme ich an, weil ich versuche, in Tablename spähen die max (Version) zu finden, während eine neue Zeile einzufügen.

Wie kann ich diesen Ansatz funktionieren lassen, oder wenn ich total falsch liege, was ist ein besserer Ansatz?

+0

Wie wäre es eine unabhängige Sequenz schlagen automatisch die 'VERSION' Spalte zu füllen? Die 'VERSION'-Spalte wird unabhängig von Lücken eine Reihenfolge für eine 'USER_VISIBLE_NATURAL_KEY_ID' bereitstellen. Warum muss "VERSION" der nächste Wert und nicht einfach ein größerer Wert sein? – Glenn

+0

Das ist eine interessante Idee. Wie würden Sie vorschlagen, mit der Sequenzübergabe umzugehen? Beachten Sie, wenn es passiert und archivieren Sie alte Daten davor? – monknomo

+0

Wenn es jemals einen Rollover gibt, erstelle ich normalerweise eine 2-teilige Kennung basierend auf dem Datum und der Reihenfolge. Dann nach der ID, Timestamp, Seq. Legen Sie den Datums-/Zeitstempel auf eine geeignete Granularität fest, und Sie müssen sich keine Gedanken über den Sequenz-Rollover machen. – Glenn

Antwort

0

Wenn Glenn eine Antwort gemacht, ich würde es akzeptieren, aber anstatt dass habe ich gewählt a machen Sequenz, um Version zu ziehen. Ich haben auch eine Ansicht, die Version eine sequentielle Reihe beginnend bei 1.

So etwas wie

SELECT 
    id, 
    user_visible_natural_key_id, 
    (SELECT count(b.id) 
    FROM tablename b 
    WHERE b.id < a.id 
    AND b.database_created_datetime <= a.database_created_datetime 
    AND b.version < a.version 
    AND b.user_visible_natural_key_id = a.user_visible_natural_key_id) + 1 
    AS version, 
value, 
database_created_datetime 
FROM tablename a 

sein abstrahiert, die mir etwas gibt, gebe ich Version 1 oder 2 auswählen können.

ich nicht ganz zufrieden bin mit meiner Sicht noch nicht, aber ich werde diese Antwort aktualisieren, wie ich auf sie ein wenig

0

Ich würde stattdessen Flashback Data Archive Funktionalität von Oracle zur Verfügung, da Oracle-Version der Suche in 11.

Es können Sie Ihre Tabelle sehr natürlich modellieren (keine Notwendigkeit für eine Version Spalte oder einem Trigger), während sehr leicht Hinzufügen konfigurierbare Änderungsverlaufsverfolgung Sie können dann Flashback-Abfragen verwenden, um vergangene Daten anzuzeigen. Es ist ziemlich mächtig.

+0

Ein Hinweis: Flashback Datenarchiv erfordert Oracle EE. Ich empfehle diese Funktion nur seit Oracle 12c. Ich sehe riesige Mengen von Fehlern und Einschränkungen in 11g. –

+0

@Evgeniy: Danke für die Rückmeldung. Über die Funktion, die nur in EE verfügbar ist, kann ich sie jetzt nicht überprüfen, aber basierend auf dem, was ich gelesen habe, sollte die Funktion in allen Editionen kostenlos zur Verfügung stehen. Es stimmt zwar, dass Sie in EE auf erweiterte Funktionen zugreifen können. Siehe den Untertitel [Lizenzänderungen (Alle Datenbankversionen kostenlos)] (https://oracle-base.com/articles/12c/flashback-data-archive-fda-enhancements-12cr1). – sstan

+0

Scheint, du hast Recht. Ich habe mein Wissen über Lizenzierung auf diesem Link https://docs.oracle.com/database/121/DBLIC/editions.htm#DBLIC109 basiert. In diesem Artikel geschrieben wird "Basic Flashback Data Archive ist in allen Editionen. Optimierung für Flashback Data Archive erfordert EE und die erweiterte Komprimierung Option", die ich vermisse –

1

Statt Versionsnummer in Ihrer Tabelle können Sie versuchen, wirksam ab Datum und effektiv bis Datum zu implementieren. Beim Einfügen einer neuen Version für einen Datensatz aktualisieren Sie die bis heute des alten Datensatzes auf SYSDATE und für die neue Version aktualisieren Sie die ab Datum als SYSDATE und wirksam bis heute als NULL. Der Rekord mit einem bis jetzt gültigen NULL ist dein letzter Rekord. Bei diesem Ansatz können Sie auch eine Versionsnummer (logisch) mit SQL-Abfragen generieren (falls Sie diese Versionsnummer irgendwo in einem Bericht anzeigen möchten).

Aus irgendeinem Grund, wenn Sie Versionsnummer in Ihrer Tabelle beibehalten möchten, wäre eine Sequenz eine bessere Wahl. Erstellen Sie für jede solche Tabelle eine Sequenz, und fügen Sie beim Einfügen eines neuen Datensatzes sequence.NEXTVAL in Ihre insert-Anweisung ein. Auf diese Weise vermeiden Sie den Overhead des Triggers. In diesem Fall wird Ihnen jedoch keine lückenlose Version garantiert (falls jemand die Transaktion nach dem Einfügen rückgängig macht, erzeugt die Sequenz Lücken).

+0

Das ist ähnlich wie etwas, das ich implementiert habe, mit dem ich nicht völlig zufrieden bin. Ich habe ein "database_create_date" und eine Ansicht, die die Version durch Zählen abliest, wie viele Daten diesem Datum vorausgingen. Es hat nur eine Millisekunden-Auflösung, manchmal bekommen wir eine Versionsnummer-Kollision, was bei uns nicht funktioniert. Wie gehen Gültigkeitsdaten mit zwei simultanen Einfügungen (oder Aktualisierungen) um? – monknomo

1

Ich werde versuchen, auf die Frage zu antworten "Wie kann ich diesen Ansatz arbeiten lassen?". Der Hauptstörer ist die Bedingung "Immer wenn ich einen Datensatz einlege, sollte der neue Datensatz eine Version haben, die um eins größer ist als die vorherige maximale Version". Wir können also keine Sequenzen verwenden, um mit Gleichzeitigkeit umzugehen. Stattdessen mussten wir eine explizite Sperre anfordern (zB exklusive Sperre für die erste Version des Objekts). Besitzer des Schlosses, der in der Lage ist, die maximale Version dieses Objekts ohne Risiko auszuwählen, um nicht die tatsächlichen Daten zu erhalten. So können Sie select for update + insert anstelle von insert + Trigger zum Beispiel verwenden. Oder verwenden Sie die Paket-API zum Erstellen neuer Versionen des Objekts.

Upd: Beispiel für eine einfache Tabelle tab(id, val, ver)

select * from tab where id = :id and ver = 0 for update;

insert into tab(id, val, ver) values(:id, :new_val, (select max(ver) + 1 from tab where id = :id));

+0

Könnten Sie erweitern auf Wählen Sie für Update + einfügen? Ich bin mir nicht sicher, ob ich es verstehe – monknomo