Announcement

Collapse
No announcement yet.

Datenänderung in Trigger noch nicht fertig

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • #16
    Originally posted by Wernfried View Post
    Das "Mutating Tables Problem" bedeutet vereinfacht, dass du eine Tabelle selektieren möchtest während sie verändert wird - das geht nicht.

    In der Funktion "fn_Suche" wird der alte Datensatz gelesen, weil du ein PRAGMA AUTONOMOUS_TRANSACTION machst.
    PRAGMA AUTONOMOUS_TRANSACTION bedeutet im Prinzip dass du für diese Funktion eine neue Oracle Session erzeugst.
    Wenn sich ein anderer User auf die DB einloggt, kann er deine Änderungen auch nicht sehen bevor du ein COMMIT gemacht hast. Der autonomen Funktion "fn_Suche" ergeht es genauso.

    Gruss
    Ok, jetzt verstehe ich den PRAGMA Befehl besser! Damit hast Du mein Dilema beschrieben und wie komme ich da raus? Wie kann ich es machen, dass die Funktion erst nach dem Commit der Tabelle_B ausgelöst wird? Lasse ich Pragma weg, hagelt es ja Fehlermeldungen.

    Danke
    Urwi

    Comment


    • #17
      Es sind schon wieder einige Antworten aufgelaufen und der Nebel lichtet sich offenbar.

      Fazit: Du kannst das so nicht machen. Auch wenn Dein Anliegen nachvollziehbar ist.

      hier ist ein Link, der das Problem beschreibt und einen Lösungsansatz bietet:
      http://wikis.gm.fh-koeln.de/wiki_db/...-Table-Problem
      http://wikis.gm.fh-koeln.de/wiki_db/...roblem,Loesung

      Die einfachste Lösung wäre, die Aufbereitung zu entkoppeln. Du hast ja ein fertige Funktion, die die "Arbeit" erledigt. Wenn Du die alle 5 Minuten aufrufst, hast Du eine Lösung. Man müsste hier nur genau schauen, dass man dabei Ressourcen schonend vorgeht, weil die spezifischen ID aus dem Trigger dann unbekannt sind. Etwa sowas wie, nimm alle Änderungen der letzten 5 minuten und berechne das Suchfeld neu.
      Gruß, defo

      Comment


      • #18
        Originally posted by Urwi View Post
        Wenn ich das richtig verstehe, handelt es sich hier um eine "kosmetische Operation" oder könnte ich mit new/old in einen Fehler aufen?
        Ich vermute das ist ein Missverständnis. Die Verwendung von :new/old ist per default möglich, ohne dass die REFERENCING section angegeben werden muss. Wenn Du sie aber angibst, dann weil Du statt new/old andere Bezeichner verwenden möchtest, eben z.B. Neu/Alt.
        Also lass Referencing .. weg oder verwende dort andere Bezeichner. Für die Funktionalität macht es aber keinen Unterschied.
        Gruß, defo

        Comment


        • #19
          Originally posted by defo View Post
          Ich vermute das ist ein Missverständnis. Die Verwendung von :new/old ist per default möglich, ohne dass die REFERENCING section angegeben werden muss. Wenn Du sie aber angibst, dann weil Du statt new/old andere Bezeichner verwenden möchtest, eben z.B. Neu/Alt.
          Also lass Referencing .. weg oder verwende dort andere Bezeichner. Für die Funktionalität macht es aber keinen Unterschied.
          Danke!
          Urwi

          Comment


          • #20
            Ja, mein Code funktioniert exakt so wie ich es (mit Ausgaben) angegeben habe (ORACLE 11.2.0.4), rein technisch sehe ich auch keinen Grund warum das (mit PRAGMA AUTONOMOUS_TRANSACTION und COMMIT) nicht funktionieren sollte.
            Wie im Thread schon geschrieben gibt es gute Gründe es aber in einer produktiven Umgebung nicht so zu lösen.
            Zuletzt editiert von jum; 20.01.2015, 13:39.

            Comment


            • #21
              Hallo jum,

              warum auch immer, bei mir funktioniuert es nicht (Oracle 11.2.0.2).

              vg

              Comment


              • #22
                Originally posted by Urwi View Post
                warum auch immer, bei mir funktioniuert es nicht (Oracle 11.2.0.2).
                Wie schon geschrieben, der Code von JUM erzeugt die Trigger erst nach dem Insert. Der Aufruf der Funktion in seinem Beispiel erfolgt dann unabhängig vom Insert per Select.
                Das ist überhaupt kein Problem, spannend wird es ja erst, wenn der Trigger da ist und weitere Insert/Update/Delete folgen.
                Gruß, defo

                Comment


                • #23
                  Nur rein SQL-technisch hier mal ein funktionierendes Beispiel mit BEFORE TRIGGER (für INSERT), ganz ohne Funktion, AUTONOMOUS TRANSACTION und die daraus resultierenden Probleme als Anregung.
                  In die Produktion würde ich ihn dennoch nicht nehmen. Eventuell kann das eigentlich zugrundeliegende Problem ja auch mit View oder MV gelöst werden...
                  Code:
                  DROP   TABLE tmp_Tabelle_A;
                  
                  CREATE TABLE tmp_Tabelle_A (
                    id   NUMBER,
                    name VARCHAR2(4000)); 
                  
                  INSERT INTO tmp_Tabelle_A (id, name) VALUES (1, '###');  
                  
                  DROP   TABLE tmp_Tabelle_B;
                  
                  CREATE TABLE tmp_Tabelle_B (
                    id   NUMBER,
                    aId  NUMBER,
                    name VARCHAR2(4000)); 
                  
                  
                  CREATE OR REPLACE TRIGGER tmp_trg_suche_before
                   BEFORE 
                   INSERT  ON tmp_Tabelle_B
                   FOR EACH ROW 
                  DECLARE
                    sText VARCHAR2( 1000 );
                    
                  BEGIN
                     SELECT listagg(name,' ') WITHIN GROUP (ORDER BY id) INTO sText FROM tmp_Tabelle_B WHERE aid=(:NEW.aid); 
                     sText := sText || ' '  || (:NEW.name) ;
                     UPDATE tmp_Tabelle_A SET name = sText WHERE Id = (:NEW.aid) ;
                  END;
                  
                  show errors
                  Dann die Tests:
                  Code:
                  SELECT name vor0 FROM tmp_tabelle_A;
                  
                  INSERT INTO tmp_Tabelle_B (id, aid, name) VALUES (1001, 1, 'Eins');  
                  INSERT INTO tmp_Tabelle_B (id, aid, name) VALUES (2001, 1, 'Uno');  
                  INSERT INTO tmp_Tabelle_B (id, aid, name) VALUES (3001, 1, 'One');  
                  
                  SELECT name nach1 FROM tmp_tabelle_A;
                  
                  INSERT INTO tmp_Tabelle_B (id, aid, name) VALUES (4001, 1, 'un');  
                  
                  COMMIT;
                  
                  SELECT name nach2 FROM tmp_tabelle_A;
                  
                  INSERT INTO tmp_Tabelle_B (id, aid, name) VALUES (5001, 1, 'odin');  
                  
                  INSERT INTO tmp_Tabelle_B (id, aid, name) VALUES (6001, 1, 'unos');  
                  
                  SELECT name nach3 FROM tmp_tabelle_A;
                  
                  ROLLBACK;
                  
                  SELECT name nach4 FROM tmp_tabelle_A;
                  Und die Ergebnisse
                  Code:
                  VOR0                                                                            
                  --------------------------------------------------------------------------------
                  ###                                                                             
                  1 row selected.
                  1 row created.
                  1 row created.
                  1 row created.
                  
                  NACH1                                                                           
                  --------------------------------------------------------------------------------
                  Eins Uno One                                                                    
                  1 row selected.
                  1 row created.
                  Commit complete.
                  
                  NACH2                                                                           
                  --------------------------------------------------------------------------------
                  Eins Uno One un                                                                 
                  1 row selected.
                  1 row created.
                  1 row created.
                  
                  NACH3                                                                           
                  --------------------------------------------------------------------------------
                  Eins Uno One un odin unos                                                       
                  1 row selected.
                  Rollback complete.
                  
                  NACH4                                                                           
                  --------------------------------------------------------------------------------
                  Eins Uno One un                                                                 
                  1 row selected.
                  Zuletzt editiert von jum; 21.01.2015, 08:42.

                  Comment


                  • #24
                    Originally posted by defo View Post
                    Die einfachste Lösung wäre, die Aufbereitung zu entkoppeln. Du hast ja ein fertige Funktion, die die "Arbeit" erledigt. Wenn Du die alle 5 Minuten aufrufst, hast Du eine Lösung. Man müsste hier nur genau schauen, dass man dabei Ressourcen schonend vorgeht, weil die spezifischen ID aus dem Trigger dann unbekannt sind. Etwa sowas wie, nimm alle Änderungen der letzten 5 minuten und berechne das Suchfeld neu.
                    Hallo defo,

                    ich muss auf deine vorgeschlagene Lösung zurückkommen, weil ich keine Möglichkeit sehe, mein Vorhaben so wie gewollt durchzuführen. Ich habe mich inzwischen ein wenig weitergespielt und hatte die Lösung schon vor Augen, die dann leider beim Update von mehreren Datensätzen nicht mehr funktioniert hat

                    Zurück zu deiner Idee. Ich könnte also in allen betroffenen Tabellen einen Trigger erstellen, der bei jeder Manipulation der Daten die Id in eine neu zu erstellende Tabelle stellt und meine Funktion dann in regelmäßigen Abständen über diese Tabelle durchführen lassen. Damit habe ich zwar das Risiko, dass das betroffene Feld nicht sofort aktuell ist, aber wenn ich alle Minuten aktualisiere, dann sollte das in der Praxis schon ausreichend sein.

                    Da ich inzwischen gelernt habe, dass nicht alles so funktioniert, wie es sich der kleine Maxi (ich) vorstellt, will ich vorher abchecken, in welche Falle ich diesmal laufen könnte.

                    Danke
                    Urwi

                    Comment


                    • #25
                      Du solltest chronologisch Jobs aufrufen können.
                      Dazu kannst Du
                      a) Dein Betriebssystem verwenden
                      b) Deine Oracle Datenbank via dbms_job .. ~ scheduler oder so
                      Kannst Du üben und Dir zyklisch mails schicken oder logfile einträge produzieren oder einen Datensatz in eine Tabelle tüten. Irgendetwas in der Art kannst Du Dir aufbewahren, um später Deinen "Index" Job oder was noch alles zu kontrollieren.

                      Du solltest einen überblick und schließlich Kontrolle über die min max Laufzeit Deines Jobs haben
                      a) es sollte sowieso schonend gearbeitet werden
                      b) es sollte nicht länger dauern, als Dein Interval
                      c) falls doch, muss das erkannt werden und natürlich auf jeden Fall abgebrochen werden!

                      so grob erstmal
                      Gruß, defo

                      Comment


                      • #26
                        Originally posted by defo View Post
                        Du solltest chronologisch Jobs aufrufen können.
                        Dazu kannst Du
                        a) Dein Betriebssystem verwenden
                        b) Deine Oracle Datenbank via dbms_job .. ~ scheduler oder so
                        Kannst Du üben und Dir zyklisch mails schicken oder logfile einträge produzieren oder einen Datensatz in eine Tabelle tüten. Irgendetwas in der Art kannst Du Dir aufbewahren, um später Deinen "Index" Job oder was noch alles zu kontrollieren.

                        Du solltest einen überblick und schließlich Kontrolle über die min max Laufzeit Deines Jobs haben
                        a) es sollte sowieso schonend gearbeitet werden
                        b) es sollte nicht länger dauern, als Dein Interval
                        c) falls doch, muss das erkannt werden und natürlich auf jeden Fall abgebrochen werden!

                        so grob erstmal
                        Hallo Defo,

                        Durchführung wird definitiv b)

                        b) und c) sind ein guter Hinweis!

                        Danke
                        Urwi

                        Comment


                        • #27
                          Eine Sache noch, sozusagen apropos autonome Transaktionen.

                          Du hast sie ja bereits zufällig verwendet, m.E. allerdings nicht richtig bzw. nicht sinnvoll.
                          Wenn Du einen scheduler job anlegst, der zyklisch irgendwas ausführt, vielleicht auch mehrere verschiedene Aufgaben erledigt, kannst Du Dich noch mal mit dem Thema auseinandersetzen.
                          Der Scheduler Mechanismus in Oracle liefert per se einige Monitoringdaten. Du produzierst mit Deinem Job und den Aufgaben ggf. eigene Monitoringdaten. Was geschieht nun, wenn Dein Job selbst "scheitert" und die eigentliche Aufgabe (oder eine der Aufgaben) nicht aufgerufen wird? Was geschieht, wenn der Job aufgerufen wird, aber eine der Aufgaben selbst fehlschlägt? Wie wirkt sich das auf den Gesamtprozess aus oder Teilprozesse?
                          Hier können autonome Transaktionen hilfreich sein, sei es für die Verarbeitung selbst oder für Monitoringdaten.
                          Gruß, defo

                          Comment


                          • #28
                            Hallo Defo,

                            meine Funktion für den Scheduler ist als autonome Trasanktion angedacht. Sollte etwas schieflaufen, kann eigentlich nichts passieren, weil beim nächsten Durchlauf, ein paar Minuten später, werden alle nicht verarbeiteten Daten eben wieder verarbeitet. Natürlich nur wenn es sich nicht um einen generellen Fehler handelt.

                            Geplant ist, jede Änderung in einer der betroffenen Tabellen erzeugt einen Eintrag in einer "Update"-Tabelle. Diese wird dann periodisch Satz für Satz mit den Scheduler-Job abgearbeitet und nach erfolgreicher Erzeugung des Suchstring gelöscht. Deshalb waren Deine Punkte b)+c) auch ein guter Hinweis auf was ich achten sollte.

                            Wobei ich da von der Oracle-DB schon ein wenig enttäuscht bin. Wenn etwas After heißt, dann gehe ich davon aus, dass das nach Irgendetwas passiert und nicht zeitgleich. So führt sich ein After-Row-Trigger schon fast ad absurdum. Ich möchte nicht wissen, wie viele da auf etwas vertrauen, dass in Wirklichkeit nicht das liefert, wofür es gedacht ist. Aber das ist ein anderes Thema ;-)

                            Danke an alle für die Hilfe und die inspirierenden Ideen
                            Urwi

                            vg
                            Urwi

                            Comment


                            • #29
                              Ich hab mich da noch nie genauer mit beschäftigt, aber Deine Kritik ist glaub ich nicht ganz angemessen. Die Benennung dreht sich um den Triggerzeitpunkt, nicht um den Inhalt des Triggers.
                              Das Mutatingproblem dagegen betrifft die zeitlichen Abläufe innerhalb des Triggers, ich glaub auch z.B. bei mehreren After Triggern. Die Fehlermeldung besagt so ungefähr:
                              "Ich kann nicht beurteilen, was hier in diesem Trigger wann wie gemacht wird und deshalb verweigere ich den Zugriff auf die Werte, die sich hier ändern (damit keine bösen Überraschungen entstehen)".
                              Die im Link weiter oben angegebene Lösung existiert ja, setzt aber voraus, dass man die Abläufe selbst regelt (programmier).

                              Es gibt glaub ich DB, die das können, was Du suchst. Die können dann vielleicht andere Sachen nicht so gut oder vielleicht langsamer. Es liegt mir auch fern, das System in Schutz zu nehmen. Es geht mir einfach darum, sowas nicht als Blackbox zu betrachten (wie es seit einiger Zeit so gern getan wird), sondern sich soweit nötig mit den Besonderheiten auseinanderzusetzen. Am Ende ist das für mich eine echte "Best Practice" Frage. Wofür und wie wurde das System entworfen und konstruiert und wie sollte ich das berücksichtigen bei meiner Anwendung. Das Ergebnis sollte sein, dass man mit dem System arbeitet, statt dagegen.
                              Kleines allgemeines Beispiel zu dem Thema/ Best Practice: Mein (imaginärer) Porsche hat genausoviel PS wie die LKW, die überall rumfahren. Ich würde dennoch nie darauf kommen, eine Anhängerkupplung dran zu schrauben und mein (imaginäres) Mobile Home damit durch die Gegend zu ziehen.

                              Und ja, Du bist sicher nicht der einzige, der das falsch interpretiert, dafür haben die Oracles vermutlich irgendwann diesen Fehler erfunden.

                              Ich kenne bei weitem nicht alle RDBMS (so wie Oracle), aber ich denke, das System ist sehr weit vorne, mit dem was man hier umsetzen kann. An der Stelle solltest Du auch bedenken, dass man mit dem was Du da umsetzt, an einer der Kernaufgaben eines solchen Systems vorbeiarbeitet, Redundanzfreiheit. Zugegeben, Dein Anliegen ist nicht ungewöhnlich, im Grunde eigentlich nur eine Performance Optimierung.
                              Aber in dem Moment, wo man das tut, ist es eine vornehmliche Aufgabe des Entwicklers, für Konsistenz und Robustheit zu sorgen, wofür sonst das System automatisch sorgt.
                              Gruß, defo

                              Comment


                              • #30
                                Hallo Defo,

                                aus meiner Sicht, der zwar Oracle verwendet aber der tiefere Einblick fehlt, wäre After eben "nach" und nicht "während". Aber es ist müsig darüber zu diskutieren. So ist es eben!

                                "Und ja, Du bist sicher nicht der einzige..." Zumindest da sind wir uns einig!

                                vg
                                Ewald

                                Comment

                                Working...
                                X