Rollback bei Fehler in Transaktion

Hallo Postgres-Gemeinde,

ich habe folgende Problematik.

ich schreibe in einer Transaktion mehrere hundert DS in eine Tabelle. nun kommt es vor, das ein DS eine Exception verursacht, wegen z.B. PK-Verletzung oder FK-Verletzung. Postgres scheint nun automatisch ein Rollback für diese Tranaktion zu machen. Ich möchte aber, das die Transaktion zu Ende ausgeführt wird und zumindest alle anderen DS, welche i.O. sind geschrieben werden. autocommit ist angeschaltet.
getestet wurde das auf einer Postgres 7.3.4
weiß jemand, ob das bei postgres funktioniert.

vielen Dank
mwbond

Meines Verständnisses nach verletzt das, was du vorhast, das Transaktionskonzept. Entweder alles geht durch und deine Transaktion ist korrekt ausgeführt und damit die DB konsitent, oder es erfolgt eben ein Rollback.

Was du anscheinend vorhast, sind viele logische Transaktionen in eine physische gepackt. Kannst du das nicht aufsplitten in viele kleine Ts?

moin,

da hat imo domski recht. also entweder für jeden datensatz eine transaktion (wobei du das dann auch gleich weglassen kannst und sofort alles festschreiben kannst) oder du bereinigst die daten vor dem einlesen…

cu tb.

Hallo mwbond,

das Transaktionskonzept wurde entwickelt um die Inkonsistenz von Datensätzen zu vermeiden. Der Abbruch der Transaktion ist daher richtig.

Die Lösung ist doch recht einfach. Erstelle ein Kopie der Original-Tabelle ohne PK und FK und lese die Daten in die Tabelle. In einen zweiten Schritt kann man dann alle Datensätze herausfiltern, die PK und FK nicht verletzen und diese dann per Transaktion in die Original-Tabelle einfügen.



mfg

fristo

Hallo Postgres-Gemeinde,
Hallo domski und fristo,

danke für eure antworten. ihr habt natürlich alle recht, was die Philosophie einer Transaktion betrifft.

der lösungsansatz von fristo gefällt mir sehr und ich möchte meine problemstellung noch etwas verfeinern.

ich importiere aus einer XML-Datei mehrere Tabellen nacheinander. die tabelle mit den kerndaten als erstes, die folgenden tabellen referenzieren auf die kerntabelle.
in der DB sind die tabellen durch FK-Constraints an die kerntabelle gebunden.

ich möchte nun angenommen ca. 100.000 DS importieren. in den referenztabellen kann die zahl der DS geringer aber auch höher sein, da ein Kern-DS in den Ref.-Tabellen durchaus auch mehrere, aber auch keine einträge haben kann.

importiere ich nun in einer Transaktion angenommen 2.000 DS und in einer der Transaktionen geht etwas schief, fehlen mir mindestens in einer tabelle 2.000 DS obwohl ich nur einen fehlerhaften DS habe.

natürlich kann ich die DS auch einzeln in die DB schreiben, ich wollte aber vor Weinachten damit fertig sein (diese Variante ist zu zeitintensiv)


ich hoffe, ich habe mein problem ausreichend beschrieben und freue mich auf die antworten

vielen dank schon jetzt
mwbond

Hallo mwbond,

vorab mal einige Grundsätze:

  1. Wenn Du eine Datenbank entwickels, stelle grundsätzlich sicher, dass die Daten konsistent sind, ansonsten kannst Du Deine Datenbank sammt Inhalt verschrotten.

  2. Wenn noch andere zur gleichen Zeit auf die Datenbank zugreifen, arbeite grundsätzlich mit Transaktionen, ansonsten gilt Punkt 1.

  3. Wenn Du in der Enwicklung arbeitest, gehe grundsätzliche davon aus, dass unvorhergesehene Probleme auftreten, deren Beseitigung zeitintensiv ist.


Dein Problem sind die Tabellen mit den FK. Hier eine mögliche Lösung:

  1. Lösche alle FK mittels ALTER TABLE … DROP CONSTRAINT … .

  2. Copiere alle deine Daten in die Tabellen.

  3. Erstelle die FK mittels ALTER TABLE … ADD table_constraint. Tritt ein Fehler bei der Ausführung des Befehls auf, so enthält die Tabelle fehlerhafte Datensätze. Diese Datensätze müssen korregiert werden.

  4. Finde die fehlerhaften Datensätze. Wie? Ist abhängig vom vorhanden Speicher (RAM)

4a. Erstelle eine Tabelle mittel LEFT JOIN, wobei die Spalten von PK und FK den join bilden. Allen fehlerhaften Datensätze besitzen in den Spalten von PK den Wert NULL.

— oder —

4b. Erstelle eine PL/pgSQL Funktion. Arbeite mit folgender FOR-Schleife:

FOR record_oder_zeile IN anfrage LOOP
anweisungen
END LOOP]“Ohne Fleiß keine Daten”[/color]

oder

“Wenn die 5. :idea: (Kerze) brennt hast Du Weihnachten verpennt”.


Na ja, dann viel Spaß. :smiley: :smiley: :smiley:



Frohes Fest und guten Rutsch
fristo

moin,

also das kann ich nicht nachvollziehen. du schreibst doch ein programm dafür und hast eine schliefe. momentan hast du vor der schleife dein BEGIN; und danach dein COMMIT; bzw. ROLLBACK.

was ist jetzt so aufwendig, die transaktionssteuerung in die schleife zu legen und dort auszuführen? du musst doch eigentlich nur ein paar zeilen (ich sag jetzt mal max 10 zeilen) verschieben…

bei der ausführung macht das vielleicht ein paar sekunden aus aber ich glaub ja nicht mal das das soviel ausmacht.

cu tb.

also das kann ich nicht nachvollziehen. du schreibst doch ein programm dafür und hast eine schliefe. momentan hast du vor der schleife dein BEGIN]

An der Stelle fehlt ihm aber noch die Fehlerbehandlung. Er möchte ja auch die Fehlerhaften Datensätze herauspuzzeln und zumindest angezeigt bekommen, wenn ich das richtig sehe. Also entweder mehr Arbeit in potenziell fehlerhaften Code stecken oder die Mehrarbeit mit fristos Vorschlag in Angriff nehmen.

Ich würde, wenn ich mir über die Vorgehensweise nicht ganz sicher bin und es von der DB her möglich ist, step-by-step auf ner Testdatenbank arbeiten und dann jeweils erfolgreiche Schritte gleich in die Produktions-DB nachziehen.

Alles in allem ist fristos Vorschlag wahrscheinlich das schnellste, sofern dieser Datenimport nur ein einziges Mal ansteht.

Frohes Fest und guten Rutschk,
Domski

Ich gebe gerne mal ein Tip und beschreibe einen Lösungsweg, das Detaildenken überlasse ich aber den Fragenden.

Wer mehr möchte, ich bin selbständig, Aufträge sind immer herzlichst willkommen. Über die “private Nachricht” kann mich jeder über dieses Forum erreichen


Frohes Fest und guten Rutsch
fristo

Dumme Frage: Warum die Transaktion (BEGIN/COMMIT) nicht einfach komplett weglassen und die Daten einzeln eintragen?
PostgreSQL gibt bei jedem Statement einen Returncode. Wenn es nicht geklappt hat, den Datensatz z.B. in eine Datei schreiben und mit dem nächsten weitermachen.

Dann sind alle richtigen Datensätze drin, und die “kaputten” liegen fertig in einer Datei.

Klappt recht gut, ich mache das zumindest so.

Gegenfragen an Dich!

  1. Wie stellt Du bei einem Mehrbenutzerbetrieb ohne Transaktion sicher, dass die Datenbank konsistent bleibt?

  2. Wenn beim Importieren von Daten mittels des Befehls COPY, und nichts anderes verwendet ‘mwbond’, eine FK-Verletzung festgestellt wird, bricht der COPY-Befehl mit einer Exception ab. Laut Doku gibt es keine Möglichkeit eine Exception abzufangen. Kannst Du anhand einiger Zeilen Kode einmal erklären, wie Du es trotzdem schaffts, die Exception abzufangen und wie Du es schaffts beim nächsten, noch nicht gelesenden Datensatz in der Import-Datei, wieder einzusteigen?



    mfg fristo