Fehler abfangen - Funktion richtig beenden

Hallo,

ich habe eine Funktion, die innerhalb einer Schleife so einige UPDATES auf bestehende Tabellen durchführt.

CREATE OR REPLACE FUNCTION my_function () RETURNS INT AS
$BODY$
DECLARE
[…]
BEGIN
FOR i IN 0…count LOOP
query_string := ‘UPDATE […]’;
EXECUTE query_string;
query_string := ‘UPDATE […]’;
EXECUTE query_string;
query_string := ‘UPDATE […]’;
EXECUTE query_string;
[…]
END LOOP;
RETURN 1 ;
END
$BODY$
LANGUAGE ‘plpgsql’ ;

Die Berechnungen dauern auf meinem Rechner so 3,5 Tage. Bei meinem letzten Durchlauf trat dann leider am Ende der Berechnungen ein von mir nicht abgefangener Fehler auf “ERROR: cannot EXECUTE a null querystring”. Daduch dass die Funktion abgebrochen wurde, wurde KEINES der vorher in der Funktion gelaufenen updates realisiert :-((

Da ich nicht weiss, ob ich jetzt wirklich alle Fehler abgefangen habe, meine Frage:

(i)
gibt es eine Möglichkeit, die Funktion ordnungsgemäß beenden zu lassen, auch wenn irgendein Fehler auftritt, damit die vorangegangenen UPDATES durchgeführt werden können?
(ii)
gibt es eine Möglichkeit der Funktion nach jedem Schleifendurchlauf zu sagen, dass die UPDATES auch ausgeführt werden sollen?

Herzlichen Dank für Eure Hilfe,
dodo

Zuerst einmal würde ich an Deiner Stelle versuchen, das zu optimieren. Gelten die Updates alle für dieselbe Tabelle?

Zu Deiner Frage: nein, eine Funktion ist nach außen eine Transaktion, entweder alles oder nichts. Wenn das schon so umständlich ist und lange dauert, und dabei auch “Teilerfolge” im Sinne einzelner Updates akzeptabel sind, dann könntest Du aber eine Art Batch-Datei schreiben, wo die einzelnen Updates jeweils gefolgt von COMMIT drin stehen, und das dann via psql ausführen lassen.

Andreas

Hallo,
es sind 3 verschiedene Tabellen auf denen das update durchgeführt wird - eine davon ist eben riesig. Außerdem werden noch ein paar temp Tabellen benutzt.

Die Idee mit der Batch-Datei hört sich gut an. Da ich keine Erfahrung damit habe, könntest Du mir ein Beispiel geben - wie muss das aussehen, wie baue ich die Schleife?

Herzlichen Dank,
dodo

Du schreibst eine Datei, die etwa so aussieht:

begin;
update table1 ...
commit;
begin;
update table2 ...
commit;

Dann rufst psql auf:

psql -f <Datei>

Einfach, oder?

Deine Befehle werden ja in einer Funktion generiert, wenn du diese so umschreibst, daß sie die Befehle so in der Form mit dem commits; liefert, kannst das auch so machen:


 psql -t -c "select 'select version();'" | psql -f -

(mal als Beispiel, musst halt Deine Funktion aufrufen und dessen Ausgabe an psql verfüttern.)



Andreas

Super!
reicht es also, wenn ich immer folgendes in meiner Funktion schreibe:

EXECUTE ‘Begin’;
EXECUTE query_string;
EXECUTE ‘Commit’;

oder muss ich Begin und Commit in den query_string integrieren?

dodo

Nein. Innerhalb einer Funktion kannst Du kein COMMIT machen.

Andreas

Wie bekomme ich dann die Schleife hin?

Du veränderst Deine Funktion so, daß sie als Resultat einen TEXT liefert, der all die auszuführenden Kommandos enthält. Das Resultat ist dann Deine Batch-Datei.

Andreas

Mit einzelnen Commits wird das aber auch nicht schneller, eher im Gegenteil.

Andere Baustelle. Das Konstrukt mit einer Schleife solche UPDATE-Befehle auf eine große Tabelle zu ballern ist eh bedenkenswert. Da könnte es sich sicher lohnen, zu schauen, ob man das anders machen kann.

Andreas

Hallo,
ich wäre natürlich für jede Hilfe sehr dankbar (vorhin ist mein Windows nach 2 Tagen Laufzeit abgestürzt…), aber ich weiss leider nicht wie ich das Problem anders als mit kontinuierlichen updates angehen soll. Ich versuche mal ein einfaches Beispiel aufzuschreiben:

Basistabelle -----> Gruppenzuordnung:
key; Nr; wert —> Nr; gruppe_alt … gruppe_neu
1 1 a --> 1 1
2 2 a --> 2 1
3 3 a --> 3 1

4 4 b --> 4 2
5 5 b --> 4 2

6 1 c --> 1 1
7 2 c --> 2 1
8 3 c --> 3 1

9 4 d --> 4 2
10 5 d --> 5 2
11 3 d --> 3 1…2

12 1 e --> 1 1
13 2 e --> 2 1
14 3 e --> 3 2…1

Ziel ist es nun, die Nr anhand des Wertes zu Gruppen zusammen zu fassen, d.h. zunächst bis key 8 sind 1,2 und 3 in einer Gruppe, z.B. 1 unterwegs, danach gehört 3 aber zu einer anderen Gruppe (4,5,3).
Mein Algorithmus verfolgt nun die Gruppen, d.h. es wird eine Teilgruppe je Wert gebildet und geschaut, ob irgendwann “gruppenfremde” darin enthalten sind. Wenn ja, wird die Gruppenbezeichnung für alle Nr dieses Wertes übernommen, von der am meisten vorhanden sind. Im obigen Bsp. bekommt damit Nr.3 die Gruppennummer 2 ab key = 11.

Mein Algorithmus geht von einer initialen Gruppenzuordnung aus, sammelt dann alle Werte, bei denen Gruppenfremde drinnen sind (im Bsp. also d) und arbeitet die dann in einer Schleife ab. Bei einer Veränderung der Gruppenzuordnung werde alle Einträge der betreffenden Nummern der Gruppe ab dem betrachteten key auf die neue Gruppenzuordnung gesetzt und die dadurch wegfallenden Werte, bei denen jetzt keine Gruppenfremde mehr drinnen sind, gelöscht.

Puh, ich weiss nicht, ob das jetzt verständlich ist, aber ein Versuch ist’s ja wert - meine “Verzweiflung” wächst… . Ich muss das ganze nur einmal für ca. 116.000.000 Datesätze duruchführen.

Also ich hab es drei Mal durchgelesen und leider nicht verstanden.