pel Geschrieben 21. Mai 2008 Teilen Geschrieben 21. Mai 2008 Hallo, 1.)Ich habe z.B. diesen Trigger: anzahl ist eine Tabellenspalte. BestellPos ist die TAbelle Aufgabenstellen: Nachdem Tupel gelöscht wird soll von diesem Tupel in der Spalte anzahl der Wert in die variable varanzahl geschrieben werden. Ist der code richtig so? bekomme keinen compile-error aber auch keine Ausgabe siehe unten... CREATE OR REPLACE TRIGGER TRIGGER1 After DELETE ON BESTELLPOS declare varanzahl number; BEGIN dbms_output.enable; select anzahl into varAnzahl from bestellpos; dbms_output.put_line('anzahl: '|| varanzahl); END; 2.)Ich suche das Ausgabefenster in dem sql developer oracle tool wo die Ausgabe "varanzahl" stehen müsste finde aber nichts? Handbuch, HIlfe , google halfen nicht weiter... Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
pel Geschrieben 21. Mai 2008 Autor Teilen Geschrieben 21. Mai 2008 Edit: sorry für neuen post, konnte nicht editieren? 3.) Wie kann ich werte aus Spalten auslesen dessen Zeile gelöscht wurde, ich aber nicht weiß welche Zeile gelöscht wurde? Dies steht ja erst zur Laufzeit fest... Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
dr.dimitri Geschrieben 21. Mai 2008 Teilen Geschrieben 21. Mai 2008 (bearbeitet) Nachdem Tupel gelöscht wird soll von diesem Tupel in der Spalte anzahl der Wert in die variable varanzahl Wenn Du dir das durchließt - kommt dir das nicht seltsam vor? Nach der Löschung willst Du etwas aus der Tabelle selektieren? Wo nichts ist kann auch nichts werden um es mal so auszudrücken Dazu gibt es in Triggern die new und old Variablen, mit denen Du auf den alten und neuen Wert zugreifen kannst( wobei old bei einem einem Insertrigger sinnigerweise nicht vorhanden ist ebensoenig wie new bei einem delete trigger) Allerdings funktioniert das nur, wenn du einen Rowlevel Trigger anlegst. Dein Tigger muss also so aussehen: CREATE OR REPLACE TRIGGER TRIGGER1 After DELETE ON BESTELLPOS [b]FOR EACH ROW[/b] BEGIN dbms_output.put_line('anzahl: '||:old.anzahl); END; / Handbuch, HIlfe , google halfen nicht weiter... Das kann ich fast nicht glauben. Suchst du in der SQLDeveloper (Version 1.5) Hilfe nach dbms_ouptut, ist es der 3.Link (Using The SQL Worksheet) Dort gibt es einen Reiter DBMS_OUTPUT (klingt auch logisch oder?) Jetzt musst nur doch die kleine Sprechblase drücken damit gepollt wird. Das Intervall kannst über den Schieberegler einstellen. Dim Bearbeitet 21. Mai 2008 von dr.dimitri Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
pel Geschrieben 22. Mai 2008 Autor Teilen Geschrieben 22. Mai 2008 (bearbeitet) super danke dir ich suchte immer nach output nicht dbms_output kannst du mir sagen wie ich innerhalb dieses trigger nicht auf die tabelle Bestellpos sondern auf eine andere Tabelle bzw. deren Spalte Betrag in der DB zugreifen kann nennen wir sie: Rechnung z.B. Declare my_rechnung Rechnung%ROWTYPE Begin my_rechnung.Betrag (würde das so stimmen) ?? obwohl ich nicht: REFERENCING OLD AS old FOR EACH ROW ganz oben angab, funkioniert der Trigger vom 1.post, woher kommt das? Bearbeitet 22. Mai 2008 von pel Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
dr.dimitri Geschrieben 22. Mai 2008 Teilen Geschrieben 22. Mai 2008 ganz oben angab, funkioniert der Trigger vom 1.post, woher kommt das? Das sind Standardwerte. Zu sagen, dass man OLD als old ansprechen möchte ist relativ sinnfrei. (würde das so stimmen) ?? Nein. In einer Datenbank greifst Du auf Daten mittels SELECT zu. Sprich Du musst das was Du haben möchtest in eine Variable selektieren. SELECT spalte INTO variablel FROM tabelle WHERE bedingung Allerdings sollten Tigger sparsam benutzt werden und auch so wenig wie möglich fachliche Logik enthalten. Benutze sie nicht um irgendetwas zu "abstrahieren" oder ähnliches. Dim Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
pel Geschrieben 22. Mai 2008 Autor Teilen Geschrieben 22. Mai 2008 (bearbeitet) CREATE OR REPLACE TRIGGER TRIGGER1 After Insert or Delete ON BESTELLPOS FOR EACH ROW Declare my_anzahl number; BEGIN SELECT :old.anzahl INTO my_anzahl FROM bestellpos; dbms_output.put_line('anzahl: '||my_anzahl); END; Fehler: DELETE FROM "PEL"."BESTELLPOS" WHERE ROWID = 'AAADZIAAEAAAAD9AAD' AND ORA_ROWSCN = '448030' and ( "POSNR" is null or "POSNR" is not null ) One error saving changes to table "PEL"."BESTELLPOS": Row 4: ORA-04091: Tabelle PEL.BESTELLPOS wird gerade geändert, Trigger/Funktion sieht dies möglicherweise nicht ORA-06512: in "PEL.TRIGGER1", Zeile 6 ORA-04088: Fehler bei der Ausführung von Trigger 'PEL.TRIGGER1' Ich weiß das ist ein trigger Problem wegen mutating table bla und sollte mit new und old gelöst werden doch ich habe ja oben :old.anzahl ?? Bearbeitet 22. Mai 2008 von pel Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
dr.dimitri Geschrieben 22. Mai 2008 Teilen Geschrieben 22. Mai 2008 Stop, stop. Du musst unterschieden zwischen PL/SQL (eine prozedurale Programmiersprache) und SQL (eine Abfragesprache). Obwohl man in PL/SQL eine SQL Abfrage ohne Probleme einfach so einbauen kann, sind das doch zwei Welten. Es finden intern auch ein Kontextswitch statt. old ist eine PL/SQL variable. Um sie zuzuweisen brauchst Du nur den := Operator. Kein SELECT INTO. Möchtest Du Daten aus einer Tabelle in einer Variable lesen brauchst Du ein SELECT INTO oder einen Cursor wenn es sich um mehr als einen Wert handelt. Was Du machen möchtest sieht also so aus: anzahl:=:old.anzahl; Ich weiß das ist ein trigger Problem wegen mutating table Das ist kein Triggerproblem, sondern eine Schutzmaßnahme, die von Oracle bewußt eingebaut wurde um dich davor zu bewahren aus einem Trigger heraus per SQL auf seine eigene Tabelle zuzugreifen. Dim Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
pel Geschrieben 22. Mai 2008 Autor Teilen Geschrieben 22. Mai 2008 (bearbeitet) Das ist kein Triggerproblem, sondern eine Schutzmaßnahme, die von Oracle bewußt eingebaut wurde um dich davor zu bewahren aus einem Trigger heraus per SQL auf seine eigene Tabelle zuzugreifen. hehe das hat halt unsere Lehrerin behauptet die eh kein plan hat... Ich poste mal die gesamte aufgabe damit klarer wird was ich will: Teillösung in worten... : der temporäre Betrag ist = Rechnung.betrag * Bestellpos.anzahl Wo ART_Code = alter ART_Code ist ? danach: neuer Rechnung.betrag = alter Rechnung.betrag - temporärer Betrag In Worten und von der Logik dürfts hinkommen doch wie ich das in pl/sql ausdrücke komme ich net weiter, da die Unterlagen die ich hier habe unvollständig und verwirrend sind und so fühle ich mich auch... Frage: Wie greife ich auf das "old" einer andere Tabelle als Bestellpos zu ? Kennst du vielleicht einfach gehaltene trigger beispiele zum durchlesen etc? Bearbeitet 22. Mai 2008 von pel Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
dr.dimitri Geschrieben 22. Mai 2008 Teilen Geschrieben 22. Mai 2008 Also eigentlich ist das die Art von Triggern, die man nie nie nie machen sollte. Logik in einem Trigger verstecken ist immer schlechtes Design und macht eine Anwendung schwer wartbar. Und sowas lernt ihr dann. Seufz. Was Du brauchst ist ein einfacher UPDATE: UPDATE rechnung SET betrag=betrag-(select :old.anzahl*preis from artikel where art_code=:old.art_code) WHERE rechnungsnr=:old.rechnungsnr So in etwa sollte das laufen (ungetestet). Aber wie gesagt: Solltest Du sowas mal im Beruf machen, hof ich, dass dir ein Kollege das ganze sofort um die Ohren haut. Dim Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
pel Geschrieben 22. Mai 2008 Autor Teilen Geschrieben 22. Mai 2008 (bearbeitet) Also eigentlich ist das die Art von Triggern, die man nie nie nie machen sollte. Logik in einem Trigger verstecken ist immer schlechtes Design und macht eine Anwendung schwer wartbar. Und sowas lernt ihr dann. Seufz. Was Du brauchst ist ein einfacher UPDATE: UPDATE rechnung SET betrag=betrag-(select :old.anzahl*preis from artikel where art_code=:old.art_code) WHERE rechnungsnr=:old.rechnungsnr So in etwa sollte das laufen (ungetestet). Aber wie gesagt: Solltest Du sowas mal im Beruf machen, hof ich, dass dir ein Kollege das ganze sofort um die Ohren haut. Dim ne ich wills ja so wie du machen ;-) ok danke dir geht! Frage: So müsste es doch auch gehen oder? mir gefällt die Syntax besser... UPDATE rechnung SET betrag = betrag - (select :old.anzahl*preis from artikel where art_code=:old.art_code AND rechnungsnr=:old.rechnungsnr); Ausgabe: DELETE FROM "PEL"."RECHNUNG" WHERE ROWID = 'AAADZEAAEAAAADdAAB' AND ORA_ROWSCN = '452495' and ( "RECHNUNGSNR" is null or "RECHNUNGSNR" is not null ) also gehts net weißt du warum? ...zu NULL nicht möglich... hm... One error saving changes to table "PEL"."RECHNUNG": Row 2: ORA-01407: Aktualisieren von ("PEL"."BESTELLPOS"."RECHNUNGSNR") zu NULL nicht möglich Bearbeitet 22. Mai 2008 von pel Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
dr.dimitri Geschrieben 22. Mai 2008 Teilen Geschrieben 22. Mai 2008 Dein Update hat ein paar Schönheitsfehler. Zum einen gibts keine einschränkende WHERE-Bedingung im UPDATE. Sprich würde das ganze (theoretisch) funktionieren, würdest Du alle Sätze ind er tabelle auf den gleichen Wert setzen. Und zwar immer wenn der Trigger zündet. Zum anderen greifst Du im Subselect auf eine Spalte des bereits gelöschten Satzes zu. Du musst unterscheiden zwischen der Spalte rechnungsnr in der Tabelle RECHNUNG und BESTELLPOS Vielleicht wird es verständlicher mit einem Alias: UPDATE rechnung rech SET rech.betrag=rech.betrag-(select :old.anzahl*preis from artikel art where art.art_code=:old.art_code) WHERE rech.rechnungsnr=:old.rechnungsnr Dim Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
pel Geschrieben 22. Mai 2008 Autor Teilen Geschrieben 22. Mai 2008 Vielleicht wird es verständlicher mit einem Alias: UPDATE rechnung rech SET rech.betrag=rech.betrag-(select :old.anzahl*preis from artikel art where art.art_code=:old.art_code) WHERE rech.rechnungsnr=:old.rechnungsnr Dim verständlicher net doch ich denke ich habs zu 70% ^^ kapiert jetzt: betrag - (wert wird ausgerechnet mit altem art_code) ABER nur da wo die alte rechnungsnr gleich der rechnungsnr in der Rechnung Tabelle ist wird upgedatet. Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
dr.dimitri Geschrieben 22. Mai 2008 Teilen Geschrieben 22. Mai 2008 betrag - (wert wird ausgerechnet mit altem art_code) ABER nur da wo die alte rechnungsnr gleich der rechnungsnr in der Rechnung Tabelle ist wird upgedatet. Genau. Und richte Deiner Lehrerin aus, dass sie die FIAE's nicht schon in der Schule verderben soll. In einem Trigger kann man Historisierungsdaten wegschreiben, vielleicht einen Timestamp setzen, einen PK über eine Sequenze füllen aber das Berechnen eines fachlichen Wertes in einer anderen Tabelle - grauenhaft. Stell dir das in einer Anwendung mit 200 und mehr Tabellen vor. Nach 2 Monaten weiß keiner mehr wo was wie und wann gesetzt und berechnet wird. Aber was red ich: Die hier solltens eigentlich wissen und machen den gleichen Schmarrn. Vielleicht ein ehemaliger Schüler deiner Lehrerin? Dim Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
pel Geschrieben 23. Mai 2008 Autor Teilen Geschrieben 23. Mai 2008 Vielleicht ein ehemaliger Schüler deiner Lehrerin? für so abwegig halte ich das gar net... zu meiner vorherigen Aufgabe: UPDATE rechnung SET r_datum = SYSDATE Where rechnungsnr =:old.rechnungsnr; es scheint zu funzen mit dem DAtum oder würdest du obigen code noch umschreiben/optimieren? Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
dr.dimitri Geschrieben 24. Mai 2008 Teilen Geschrieben 24. Mai 2008 Wenn Du mit dem Update Statement das Datumsfeld einer Rechnung auf das jetztige Datum setzen möchtest, dann ist das so richtig. Dim Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
pel Geschrieben 24. Mai 2008 Autor Teilen Geschrieben 24. Mai 2008 (bearbeitet) Wenn Du mit dem Update Statement das Datumsfeld einer Rechnung auf das jetztige Datum setzen möchtest, dann ist das so richtig. Dim ok danke dir so wollte ich das auch! CREATE OR REPLACE PROCEDURE UPDATE_PRAEMIE AS eine mehr philosophische Frage... 1.) Warum steht nach jedem Methodennamen "AS" ??? Ich meine was bringt das oder Sinn? Erschaffe oder ersetze eine Prozedur soundso als ?? als was ? 2.) Ich bekomme hierbei Fehlermeldungen 2 Stück doch was ist an der Syntax falsch? CREATE OR REPLACE PROCEDURE UPDATE_PRAEMIE AS declare cursor Mitarbeiter_Cursor is select * from Mitarbeiter for update; MA_Daten Mitarbeiter%ROWTYPE; -- Die Datensatzstruktur Spalten/Datentypen befinden sich nur im Datensatz MA_Daten BEGIN OPEN Mitarbeiter_Cursor; FOR MA_Daten IN Mitarbeiter_Cursor -- Für jeden Datensatz MA_Daten der sich in dem Cursor(-Array) Mitarbeiter_Cursor befindet mache folgendes: LOOP -- **************************** if MA_Daten.MA_Praemie > 9000 then MA_Daten.MA_Praemie:= MA_Daten.MA_Praemie * 1.15; -- 15 % mehr Praemie elsif MA_Daten.MA_Praemie > 5000 then MA_Daten.MA_Praemie:= MA_Daten.MA_Praemie * 1.1; -- 10 % mehr Praemie elsif MA_Daten.MA_Praemie > 1000 then MA_Daten.MA_Praemie:= MA_Daten.MA_Praemie * 1.05; -- 5 % mehr Praemie end if; END LOOP; -- *************************** CLOSE Mitarbeiter_Cursor; END UPDATE_PRAEMIE; Fehler: Error(4,3): PLS-00103: Fand das Symbol "DECLARE" als eines der folgenden erwartet wurde: begin function package pragma procedure subtype type use <an identifier> <a double-quoted delimited-identifier> form current cursor external language Das Symbol "begin" ersetzte "DECLARE", um fortzufahren. Error(22,54): PLS-00103: Fand das Symbol "end-of-file" als eines der folgenden erwartet wurde: begin case declare end exception exit for goto if loop mod null pragma raise return select update while with <an identifier> <a double-quoted delimited-identifier> <a bind variable> << close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe Alternative else if Lösung: update Mitarbeiter set MA_Praemie = MA_praemie * 1.15 where current of Mitarbeiter_Cursor Ich habe gegoogelt verstehe das where current of <cursorname> trotzdem nicht... kann mir das jemand kurz erklären? grob übersetzt: update Mitarbeiter Tabelle und dessen Spalte MA_Praemie mit 15 % wo der Cursor aktuell draufzeigt ?? Ist das so richtig gedünkt? Bearbeitet 24. Mai 2008 von pel Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
pel Geschrieben 24. Mai 2008 Autor Teilen Geschrieben 24. Mai 2008 (bearbeitet) ok ist jetzt nur noch 1 Fehler ^^ Frage: Das unten ist Muster-Code-Lösung... declare cursor cur_mitarbeiter is select * from i33mitarbeiter for update; MA_Daten i33mitarbeiter%ROWTYPE; müsste der hintere codeteil nicht so heißen: MA_Daten cur_mitarbeiter%ROWTYPE ?? grrrr... EDIT: OK habe den Fehler gefunden es fehlte ein BEGIN UNd habe die Prozedur ausgeführt: bekomme diese Meldung im Log: Connecting to the database MyConnection. ORA-06511: PL/SQL: Cursor wurde bereits geöffnet ORA-06512: in "PEL.UPDATE_PRAEMIE", Zeile 5 ORA-06512: in "PEL.UPDATE_PRAEMIE", Zeile 11 ORA-06512: in Zeile 2 Prozess beendet. Disconnecting from the database MyConnection. Beispiel error: ORA-06512 at string line string Cause: Backtrace message as the stack is unwound by unhandled exceptions. Action: Fix the problem causing the exception or write an exception handler for this condition. Or you may need to contact your application administrator or database administrator. Was kann das ein? Dazu muss ich sagen, dass manche Mitarbeiter eine Prämie von 0 haben. Spielt das ein Problem? Bearbeitet 24. Mai 2008 von pel Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
pel Geschrieben 24. Mai 2008 Autor Teilen Geschrieben 24. Mai 2008 Wenn ich den cursor nicht öffne gehts sprich keine Fehlermeldung mehr doch die Prämien werden nicht geupdatet... ABER man muss doch einen Cursor öffnen das verstehe ich jetzt net... Weiterhin verwirrt mich und ich habe auf Wikipedia geschaut: MA_Daten Mitarbeiter_Cursor%ROWTYPE; Mitarbeiter_Cursor ist ein Cursor! Lehrerin schreibt in der Lösung die net funzt...: MA_Daten Mitarbeiter%ROWTYPE; Mitarbeiter ist eine Tabelle! Wie ist die Syntax jetzt richtig? MA_Daten <Tabellename>%ROWTYPE oder MA_Daten <Cursorname>%ROWTYPE ?? Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
dr.dimitri Geschrieben 24. Mai 2008 Teilen Geschrieben 24. Mai 2008 (bearbeitet) Also Deine Lehrerin sollte ihre Kenntnisse wirklich mal auffrischen... Fangen wir mit dem einfachen an. OPEN Mitarbeiter_Cursor; FOR MA_Daten IN Mitarbeiter_Cursor Die FOR Cursor Syntax gibt es seit 9i. Dabei übernimmt Oracle das öffnen und schließen des Cursors. Daher der Fehler weil Du den Cursor selbst öffnest und Oracle es dann nochmal versucht. Du kannst auch folgendes schreiben: FOR myrecord IN (select col1,col2,col3 FROM tabelle) LOOP ... END LOOP Ist aber etwas unübersichtlich wenn das SQL umfangreicher wird, daher verwende ich diese Syntax eher sparsam. Wie ist die Syntax jetzt richtig? Beides. Das einemal erzeugst Du einen Record vom Typ der Tabelle, das andere legt einen Record vom Typ eines Cursors an. Du brauchst wohl eher einen Record auf den Cursors, wobei der bei einem SELECT * identisch mit der Tabelle ist. if MA_Daten.MA_Praemie > 9000 then MA_Daten.MA_Praemie:= MA_Daten.MA_Praemie * 1.15; -- 15 % mehr Praemie elsif MA_Daten.MA_Praemie > 5000 then MA_Daten.MA_Praemie:= MA_Daten.MA_Praemie * 1.1; -- 10 % mehr Praemie elsif MA_Daten.MA_Praemie > 1000 then MA_Daten.MA_Praemie:= MA_Daten.MA_Praemie * 1.05; -- 5 % mehr Praemie end if; Mir wär' nicht bekannt, das diese Sytanx zu einem Update einer Tabelle führt. Sieht schön aus der Nutzwert dürfte sich aber gegen 0 bewegen. Mitarbeiter set MA_Praemie = MA_praemie * 1.15 where current of Mitarbeiter_Cursor Dies ist die richtige Syntax. Damit wird der Satz, auf welchen der Cursor aktuell "zeigt" updgedated. Allerdings hat diese Aufgabe einen gravierenden Fehler: Sie zeigt wie man es nicht machen soll. Ihr bekommt hier grade die langsamste Methode gezeigt eine Tabelle zu ändern. Erste Regel wenn man mit PL/SQL und Cursors arbeitet: Mache nichts mit PL/SQL wenn Du es mit einem einzigen Statement machen kannst: update mitarbeiter a set praemie=(select case when praemie > 9000 then praemie*1.15 when praemie > 5000 then praemie*1.1 when praemie > 1000 then praemie*1.05 end case from mitarbeiter b where a.id=b.id) Allerdings ist diese Abfrage auch etwas seltsam, denn ein Mitarbeiter der 9001 bekommt, ist sowohl größer als 1000 und 5000 und 9000. Aber das müsst ihr wissen Dim Bearbeitet 24. Mai 2008 von dr.dimitri Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
pel Geschrieben 25. Mai 2008 Autor Teilen Geschrieben 25. Mai 2008 wie du gesagt hast keine fachliche logik in einen trigger.... kann ich dann ich einem trigger eine Funktion/Prozedur aufrufen, die dann z.B. Prämien updated oder wäre das doppelt gemoppelt? Was ist denn der praktische/alltäglich Anwendungsfall von Triggern, sprich Datensatz wird gelöscht/updated/inserted und dann?? was passiert dann hier meistens? Das habe ich noch vom oracle forum gefällt mir auch gut: update Mitarbeiter set MA_Praemie = case when MA_Praemie > 9000 then MA_Praemie * 1.15 when MA_Praemie > 5000 then MA_Praemie * 1.10 when MA_Praemie > 1000 then MA_Praemie * 1.05 else MA_Praemie end also open cursor nur mit der for ...in... cursor schleife OK! Kannst du erkennen warum ich hier für eine Prämie von 8000 (+10%) einen wert von 22390.. bekomme ??? create or replace PROCEDURE UPDATE_PRAEMIE AS BEGIN DECLARE CURSOR Mitarbeiter_Cursor is select * from Mitarbeiter for update; MA_Daten Mitarbeiter%ROWTYPE; -- Die Datensatzstruktur Spalten/Datentypen befinden sich nur im Datensatz MA_Daten BEGIN --OPEN Mitarbeiter_Cursor; FOR MA_Daten IN Mitarbeiter_Cursor -- Für jeden Datensatz MA_Daten der sich in dem Cursor(-Array) Mitarbeiter_Cursor befindet mache folgendes: LOOP -- ********************************************************************************************************** -- FETCH Mitarbeiter_Cursor INTO ma_daten; update Mitarbeiter set MA_Praemie = case when MA_Praemie > 9000 then MA_Praemie * 1.15 when MA_Praemie > 5000 then MA_Praemie * 1.10 when MA_Praemie > 1000 then MA_Praemie * 1.05 else MA_Praemie end; EXIT WHEN Mitarbeiter_Cursor%NOTFOUND; END LOOP; -- ***************************************************************************************************** --CLOSE Mitarbeiter_Cursor; END; COMMIT; END UPDATE_PRAEMIE; Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
dr.dimitri Geschrieben 25. Mai 2008 Teilen Geschrieben 25. Mai 2008 wie du gesagt hast keine fachliche logik in einen trigger.... kann ich dann ich einem trigger eine Funktion/Prozedur aufrufen, die dann z.B. Prämien updated oder wäre das doppelt gemoppelt? Ja kannst Du, aber damit verlagerst Du nur das Sympthom. Ein Tigger soll nicht nur keine fachliche Logik enthalten, er soll sie auch nicht aufrufen. Streich das am besten sofort weder aus deinem Kopf. Der Update soll aus dem Programm aufgerufen werden. Der beste Weg ist, alles in eine Procedur zu stecken, die dann z.B. per Java o.ä. mit entsprechenden Parametern aufgerufen wird. Kein Trigger der dazu führt, das irgend etwas "magisches" passiert. Wie gesagt stell dir eine AW mit 200 Tabellen vor, die auf diese "Technik" zurückgreift. Dazu gibts noch 10 Entwickler und jeder schreibst so seine persönlichen Zauberkunststücke in diverse Trigger. Glaubst Du, da blickt nach einer gewissen Zeit noch einer durch? Trigger können dazu verwendet werden um z.B. Änderungen zu loggen und Historisierungstabellen mit den alten Werten zu befüllen. Es gibt bei uns ca. 250 Trigger, von denen etwa 99% über Skripte generiert werden und die nichts anderes machen als einen Timestamp zu setzen und Werte wegzuschreiben. also open cursor nur mit der for ...in... cursor schleife OK! Eigentlich sollte man nur noch FOR record IN cursor LOOP verwenden. Dann braucht man sich nicht um die Freigabe von Ressourcen kümmern - auch nicht im Fehlerfall. Dein EXIT WHEN ist ebenfalls überflüssig. PL/SQL beendet die Schleife selbst wenn ales gelesen wurde. update Mitarbeiter set MA_Praemie = case when MA_Praemie > 9000 then MA_Praemie * 1.15 when MA_Praemie > 5000 then MA_Praemie * 1.10 when MA_Praemie > 1000 then MA_Praemie * 1.05 else MA_Praemie end Ja stimmt - das von mir war natürlich doppelt gemoppelt. Das wär die korrekte Lösung. Kannst du erkennen warum ich hier für eine Prämie von 8000 (+10%) einen wert von 22390.. bekomme ??? Weil Du die Prozedure 8 mal ausgeführt hast. END; COMMIT; END UPDATE_PRAEMIE; Wenn Du das in einen Trigger einbaust gibt es einen Fehler, da in einem Trigger nicht committed werden darf. Eine weitere Schutzfunktion. ich hab übrigends vor einiger Zeit mal einen kleinen Artikel zu Cursor geschrieben. Vielleicht hilft dir das weiter: PL/SQL Cursor Tipps Dim Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
pel Geschrieben 25. Mai 2008 Autor Teilen Geschrieben 25. Mai 2008 super man deine Antworten sind echt hilfreich! Syntax von Select: SELECT column_name(s) FROM table_name warum dann das? cursor cur_mitarbeiter is select * from i33mitarbeiter for update; warum das for update hinten? würde es nicht auch so ok sein?: cursor cur_mitarbeiter is select * from i33mitarbeiter ; warum brauche ich in einer Funktion kein declare(compiler meckert) aber in einer Prozedur schon? Wo ist da der Sinn? Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
pel Geschrieben 25. Mai 2008 Autor Teilen Geschrieben 25. Mai 2008 Zitat: Kannst du erkennen warum ich hier für eine Prämie von 8000 (+10%) einen wert von 22390.. bekomme ??? Weil Du die Prozedure 8 mal ausgeführt hast. ja die prozedur habe ich nur 1x aufgerufen, doch was 8 mal passiert ist dies: das ist doch richtig so, dass für jedes element im cursor die Prämie geupdated wird, was ist daran falsch? laut meinem ergebnis müsste bei 8 tupel ja 8 durchläufe und dazu je 8 updates geschehen tuts doch aber nicht, sorry steh auf dem Schlauch FOR MA_Daten IN Mitarbeiter_Cursor -- Für jeden Datensatz MA_Daten der sich in dem Cursor(-Array) Mitarbeiter_Cursor befindet mache folgendes: LOOP -- ********************************************************************************************************** -- FETCH Mitarbeiter_Cursor INTO ma_daten; update Mitarbeiter set MA_Praemie = case when MA_Praemie > 9000 then MA_Praemie * 1.15 when MA_Praemie > 5000 then MA_Praemie * 1.10 when MA_Praemie > 1000 then MA_Praemie * 1.05 else MA_Praemie end; EXIT WHEN Mitarbeiter_Cursor%NOTFOUND; END LOOP; -- ***************************************************************************************************** Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
dr.dimitri Geschrieben 25. Mai 2008 Teilen Geschrieben 25. Mai 2008 (bearbeitet) cursor cur_mitarbeiter is select * from i33mitarbeiter for update; Mit einem FOR UPDATE sperrst Du die selektierten Sätze für andere Sessions. Sie können gelesen aber nicht mehr geändert werden bis deine Session die Transaktion mit COMMIT oder ROLLBACK beendet. Ohne FOR UPDATE könnte eine andere Session die Sätze ändern und Du müsstest warten bis Du schreiben kannst. Hättest dann aber den Wert des anderen überschrieben. Das Thema Multiuser ist ein eigenes Kapitel für sich und es gibt zwei verschiedene Ansätze dafür. Zum einen das pessimistische Locking (das was Du machst) oder das optimistische Locking (erst locken bevor geschrieben wird und dabei prüfen ob sich der Wert in der Zwischenzeit geändert hat). Meistens wird das optimitische Locking verwendet, da ansonsten immer sehr viele Datensätze für andere Sessions gesperrt wären. laut meinem ergebnis müsste bei 8 tupel ja 8 durchläufe und dazu je 8 updates geschehen tuts doch aber nicht, sorry steh auf dem Schlauch Keine Ahnung was Du da drumherumprogrammiert hast. Auf jeden Fall ist dein Cursor unnötig und versaut dir dein Ergebnis. Du brauchst keine Schleife und keinen Cursor - dein UPDATE Befehl macht alles in einem Rutsch. Dim Bearbeitet 25. Mai 2008 von dr.dimitri Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
pel Geschrieben 25. Mai 2008 Autor Teilen Geschrieben 25. Mai 2008 Keine Ahnung was Du da drumherumprogrammiert hast. Auf jeden Fall ist dein Cursor unnötig und versaut dir dein Ergebnis. Du brauchst keine Schleife und keinen Cursor - dein UPDATE Befehl macht alles in einem Rutsch. so gehts: ok ich versuchte immer eine update auf einzelne Zeilen zu machen, wobei update ja beinhaltet, dass die ganze TAbelle betroffen ist :schlaf: create or replace PROCEDURE UPDATE_PRAEMIE AS BEGIN BEGIN update Mitarbeiter set MA_Praemie = case when MA_Praemie > 9000 then MA_Praemie * 1.15 when MA_Praemie > 5000 then MA_Praemie * 1.10 when MA_Praemie > 1000 then MA_Praemie * 1.05 else MA_Praemie end; END; COMMIT; END UPDATE_PRAEMIE; meine Verwirrung insgesamt beruht auf diesen 2 Lösungen der Lehrerin... create or replace PROCEDURE I33UPDATE1_PRAEMIE AS BEGIN declare cursor cur_mitarbeiter is select * from i33mitarbeiter for update; MA_Daten i33mitarbeiter%ROWTYPE; BEGIN for MA_Daten in cur_mitarbeiter loop if MA_Daten.MA_Praemie > 9000 then MA_Daten.MA_Praemie:= MA_Daten.MA_Praemie * 1.15; -- Alternative: -- then update i33mitarbeiter set MA_Praemie = MA_praemie * 1.15 where current of cur_mitarbeiter elsif MA_Daten.MA_Praemie > 5000 then MA_Daten.MA_Praemie:= MA_Daten.MA_Praemie * 1.1; -- s.o. elsif MA_Daten.MA_Praemie > 1000 then MA_Daten.MA_Praemie:= MA_Daten.MA_Praemie * 1.05; -- s.o. end if; -- für alternative Lösung nicht: update i33mitarbeiter set MA_Praemie = MA_Daten.MA_Praemie where MA_Nr = MA_Daten.MA_Nr; end loop; end; commit; END I33UPDATE1_PRAEMIE; create or replace PROCEDURE I33UPDATE2_PRAEMIE AS BEGIN declare cursor cur_mitarbeiter is select * from i33mitarbeiter for update; MA_Daten i33mitarbeiter%ROWTYPE; BEGIN open cur_mitarbeiter; loop fetch cur_mitarbeiter into MA_Daten; if MA_Daten.MA_Praemie > 9000 then MA_Daten.MA_Praemie:= MA_Daten.MA_Praemie * 1.15; -- Alternative: -- then update i33mitarbeiter set MA_Praemie = MA_praemie * 1.15 where current of cur_mitarbeiter elsif MA_Daten.MA_Praemie > 5000 then MA_Daten.MA_Praemie:= MA_Daten.MA_Praemie * 1.1; -- s.o. elsif MA_Daten.MA_Praemie > 1000 then MA_Daten.MA_Praemie:= MA_Daten.MA_Praemie * 1.05; -- s.o. end if; -- für alternative Lösung nicht update i33mitarbeiter set MA_Praemie = MA_Daten.MA_Praemie where MA_Nr = MA_Daten.MA_Nr; exit when cur_mitarbeiter%NOTFOUND; end loop; close cur_mitarbeiter; end; commit; END I33UPDATE2_PRAEMIE; Zitieren Link zu diesem Kommentar Auf anderen Seiten teilen Mehr Optionen zum Teilen...
Empfohlene Beiträge
Dein Kommentar
Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.