Unendlichen Regress verhindern

Hallo Forum,

ich treffe immer wieder auf einen Anwendungsfall, den ich mal in ein paar
Worten kurz skizzieren möchte und für den ich noch keine wirklich “schöne”
Lösung gefunden habe:

Ausgangssituation: ich habe Angebote mit Angebotspositionen. Innerhalb der
Angebotspositionen gibt es Zwischensummen, die jeweils von den vorherigen
Positionen abhängig sind und selber Einfluss auf die nachfolgenden Positionen
haben.

Ich möchte die Zwischensummen gerne in der Datenbank und nicht im Client
berechnen, damit der Client “dumm” sein kann und keinerlei oder sehr wenig
Geschäftslogik enthält. Das macht die Umstellung auf einen anderen Client
einfach und falls verschiedene Clients im Einsatz sind (Web-Formular,
Java-Client, etc.) muss ich nicht in allen Clients Änderungen vornehmen,
wenn sich die Geschäftslogik geändert hat.

Die Berechnungen werden durch Trigger ausgelöst.

Das Problem dabei ist aber, wenn eine neue Angebotsposition hinzugefügt wurde,
oder eine bestehende geändert oder gelöscht wurde, so müssen alle nachfolgenden
Positionen geändert werden. Dadurch wird aber wieder der Trigger ausgelöst
und ich gerate unter bestimmten Umständen in einen unendlichen Regreß.

Verhindern tue ich das durch Abbruchbedingungen innerhalb der Trigger-Funktion.
Diese können je nach Anwendungsfall jedoch recht komplex sein und machen den
gesamten Code “hässlich” und schwer durchschaubar.

Gibt es eine kanonische Lösung für diesen Anwendungsfall?
Der Trigger löst “FOR EACH ROW” aus, würde hier “FOR EACH STATEMENT” helfen?
Könnte ich das vielleicht mit einem Cursor machen, den ich iterativ durchgehe
oder wird dann für jede Zeile wieder der Trigger ausgelöst ?

Das mit dem Cursor hatte ich schon mal versucht, erhielt aber die Fehlermeldung,
der Cursor könne nicht geöffnet werden, da schon in Benutzung. Sind Cursor vielleicht
global?

Ich freue mich auf Anregungen.

Gegenfrage: wieso führst du nicht nur die Rechnungspositionen in der Datenbank auf und erledigst die Berechnung beim Selektieren der Daten?
SQL Dinge wie OVER ect. (Stichwort: Windowing Functions) sollten dir da eine große Hilfe sein.

Ich möchte eigentlich keinerlei Berechnungen im Client anstellen, da ich flexibel bleiben möchte und eventuell auch mit verschiedenen Clients auf die Daten zugreifen möchte. Bei den Zwischensummen handelt es sich nicht immer um eine einfache Summenbildung: die Berechnungen können auch komplexer sein und sich durchaus auch mal ändern.

Insbesondere sollen die Nutzer die Möglichkeit haben, auch abweichende Summen einzutragen - deshalb benötige ich schon entsprechende Attribute.

Und wie ich schon ausgeführt habe: die eigentliche Geschäftslogik möchte ich komplett in der Datenbank abbilden, da mit verschiedensten Clients auf die Datenbank zugegriffen wird: Web-Formulare, Berichtsgeneratoren, Java-Clients und manchmal sogar direkt mit psql (wenn ich mal wieder was reparieren muss…). Wenn ich bei einer Änderung der Geschäftslogik alle Clients anpassen muss, kann das ein schöner Haufen Arbeit sein…

Daten in die Tabellen und Selektion und Berechnung über eine Funktion in die DB? Damit keine Logik in der Applikation und dein Problem sauber umgangen :wink:

cu tb.

Ich sagte nicht, das du das im Client machen sollst - sondern das du das beim Auslesen (in der Datenbank) berechnen sollst.

@Xandrian: meinst Du über eine SRF? Daran habe ich auch schon gedacht. Aber ich bin mir nicht sicher ob das alle Clients unterstützen, wenn man ODBC, JDBC, o.ä. verwendet.

@ads: ich verstehe jetzt nicht ganz, was Du mit “Auslesen” meinst: beim SELECT? Aber das wäre ja im Client. Es geht ja nicht nur um einfache Summenbildung. Die Berechnungen, die von Datensatz zu Datensatz propagiert werden, können recht komplex sein. Außer dem möchte ich bei einer Änderung der Geschäftslogik in den “alten” Datensätzen auch die ursprünglichen Ergebnisse erhalten…

Nun, du kannst die gesamte Logik in Stored Procedures abbilden und rufst bloß die Funktion aus deinem Client auf.

@ads: ich hatte auch schon darüber nachgedacht, das ganze als SRF zu machen - habe ich schon öfters so gemacht. Leider haben aber einige Clients Probleme damit.

Was ich eigentlich bräuchte wäre, dass ich in der Triggerfunktion alle zugehörigen Datensätze iterativ durchlaufe OHNE dass jedesmal die Triggerfunktion wieder ausgelöst wird…

Moment mal, welche Clients haben damit Probleme?
Was für Probleme hast du mit dem Aufruf von Stored Procedures?

Das Problem wirst du in der Form nicht lösen können, wie du dir das vorstellst.
Du löst immer den Trigger neu aus, wenn du die Daten änderst.

Ich weiß es nicht mehr ganz genau, bei welchem Client das Problem auftauchte, ich meine es wäre OpenOffice gewesen: ich konnte keine Abfrage der Art

SELECT feld1, feld2, feld3 FROM srf_function(param1, param2)

machen. Select ging nur auf Tabellen oder Views.

Ich habe meine Funktion überarbeitet und bin fast am Ziel: leider stolpere ich noch darüber, dass ein UNIQUE-Constraint nicht DEFERRED sein kann…

Trotzdem Danke für die Anmerkungen. Ich werde wohl an irgendeiner Stelle in den sauren Apfel beißen müssen… :frowning:

Da fällt mir glatt eine Lösung ein, die - hoffentlich - functioniert: ich kapsel die SRF in eine View als Wrapper ein…

Und updaten? :wink:

Jaaa! Ich hab’s gerade in der Doku. für 9.x gelesen: deferred geht jetzt unter anderem auch für unique()!
Da ich in den nächsten Tagen den DB-Server eh neu aufsetzen wollte gibt es jetzt einen Grund mehr… :wink: