Befehlsorientierter Trigger: Wie Zeiträume verglei

Von: , Frage gestellt am Sa, 19. Mai 2007

Hallo erstmal!

Da hätt ich auch schon ne Frage:

Bin ein ziemlicher Neuling in PL/SQL und muss nun einen Trigger schreiben, der ein RAISE_APPLICATION_ERROR ausgibt, wenn in eine vorhandene Datenbank mit Spalten "von" und "bis" neue Zeilen eingefügt werden, bei denen sich die Zeiträume mit den gegebenen Daten überschneiden.

Kann mir da jemand auf die Sprünge helfen?

mfg & Danke

12 Antworten zu dieser Frage

  1. Antwort von nach 4 Stunden 0 hilfreich
    Re: Befehlsorientierter Trigger: Wie Zeiträume ver

    hallo Bin ein ziemlicher Neuling in PL/SQL und muss nun einen
    Trigger schreiben, der ein RAISE_APPLICATION_ERROR ausgibt,
    wenn in eine vorhandene Datenbank mit Spalten "von" und "bis"
    neue Zeilen eingefügt werden, bei denen sich die Zeiträume mit
    den gegebenen Daten überschneiden.
    ich fürchte, so einfach wird das nicht. das problem ist, dass oracle es einem trigger nicht zulässt, auf die tabelle, auf die gerade zugegriffen wird, lesend zuzugreifen.

    angenommen, du hast folgende tabelle:

    create table zeitraum
    ( id   number(5) not null,
    von  date      not null,
    bis  date      not null );
    


    nett wäre nun ein trigger in der art:
    create or replace trigger ti_zeitraum
    before insert on zeitraum
    for each row
    is
    ln_anzahl  number ;
    begin
    select count(*)
    into ln_anzahl
    from zeitraum
    where :new.von between von and bis
    or :new.bis between von and bis ;
    if ln_anzahl > 0 then
    raise_application_error (-20001, 'Zeitraum überschneidet sich');
    end if ;
    end ;
    


    (keine ahnung, ob das oben prinzipiell kompilieren würde - habe gerade kein oracle zur verfügung...)

    sobald aber alle syntaxfehler und sonstigen tippfehler ausgebessert sind, wir oracle immer noch sagen, dass kein zugriff auf die eigene tabelle möglich ist (du bekommst "ORA-04091: table name is mutating, trigger/function may not see it.")


    google mal zu dem thema:
    http://www.google.at/search?client=firefox-a&rls=org...

    findest jede menge mehr oder weniger hilfreiche seiten zum thema.



    wirklich sauber lässt sich das problem nicht lösen. ich gehe meist so vor, dass ich mir eine prozedur bzw. eine package schreibe, die den zugriff auf die tabelle macht. diese prozedur kann dann ohne probleme alle checks machen, die notwendig sind. dem jeweiligen anwender nimmt man dann einfach das insert-recht auf die tabelle und gibt ihm nur das execute auf die prozedur. schon kann er nur mehr auf kontrolliertem weg auf die tabelle zugreifen.

    oder du verlagerst diesen check gleich in die anwendung - im vertrauen darauf, dass der anwender nur mit diesem programm auf die datenbank zugreifen wird.

    lg
    erwin

    • Antwort von nach 5 Stunden 0 hilfreich
      Re^2: Befehlsorientierter Trigger: Wie Zeiträume v

      Naja, soll ja ein statement-trigger sein, kein row-trigger.
      Da tritt dann ja auch kein mutating table auf. sobald aber alle syntaxfehler und sonstigen tippfehler
      ausgebessert sind, wir oracle immer noch sagen, dass kein
      zugriff auf die eigene tabelle möglich ist (du bekommst
      "ORA-04091: table name is mutating, trigger/function may not
      see it.")

      • Antwort von nach 5 Stunden 0 hilfreich
        Re^3: Befehlsorientierter Trigger: Wie Zeiträume v

        Hallo,

        du brauchst beides:

        1. Du musst eine Temporäre Tabelle anlegen, um die Werte zu sammeln.
        2. Die Temporäre Tabelle wird über einen Zeilentrigger gefüllt.
        3. Jetzt kann man die Werte wieder auslesen und die Überprüfungen machen.

        Gruß

        Peter [Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

        • Antwort von nach 6 Stunden 0 hilfreich
          Re^4: Befehlsorientierter Trigger: Wie Zeiträume v

          Erstmal Danke für die Hilfe ;)

          Leider steht in meiner Aufgabenbeschreibung explizit, das ich einen (oder mehrere) Statement-Trigger verwenden soll.
          Werd mich morgen früh mal dransetzen, dann seh ich weiter... Hallo,

          du brauchst beides:

          1. Du musst eine Temporäre Tabelle anlegen, um die Werte zu
          sammeln.
          2. Die Temporäre Tabelle wird über einen Zeilentrigger
          gefüllt.
          3. Jetzt kann man die Werte wieder auslesen und die
          Überprüfungen machen.

          Gruß

          Peter

    • Antwort von nach einem Tag 0 hilfreich
      Re^2: Befehlsorientierter Trigger: Wie Zeiträume v

      Hi! ich fürchte, so einfach wird das nicht. das problem ist, dass
      oracle es einem trigger nicht zulässt, auf die tabelle, auf
      die gerade zugegriffen wird, lesend zuzugreifen.
      Doch, doch, das Zauberwort nennt sich "Autonomous Transaction"

      Und mit Deinem Beispiel:

      create or replace trigger ti_zeitraum
      before insert on zeitraum
      for each row
      DECLARE
      PRAGMA AUTONOMOUS_TRANSACTION;
      ln_anzahl  number ;
      begin
      select count(*)
      into ln_anzahl
      from zeitraum
      where :new.von between von and bis
      or :new.bis between von and bis ;
      if ln_anzahl > 0 then
      raise_application_error (-20001, 'Zeitraum überschneidet sich');
      end if ;
      end ;
      (keine ahnung, ob das oben prinzipiell kompilieren würde -
      habe gerade kein oracle zur verfügung...)
      geht mir genauso

      Allerdings: Diese autonomen Transaktionen in Tabellen-Triggern habe ich persönlich noch nie angewandt - ein bisserl heikel ist es halt schon, den sobald mehrere Records eingefügt werden, ist der Zustand der Tabelle gar nicht mehr mal so eindeutig definierbar ...

      Grüße,
      Tomh

      • Antwort von nach einem Tag 0 hilfreich
        Re^3: Befehlsorientierter Trigger: Wie Zeiträume v

        Auch hier nochmal danke für die Hilfe, leider muss ich definitiv einen Statement Trigger verwenden :( Doch, doch, das Zauberwort nennt sich "Autonomous Transaction"

        Und mit Deinem Beispiel:

        create or replace trigger
        ti_zeitraum
        before insert on zeitraum
        for each row
        DECLARE
        PRAGMA AUTONOMOUS_TRANSACTION;
        ln_anzahl  number ;
        begin
        select count(*)
        into ln_anzahl
        from zeitraum
        where :new.von between von and bis
        or :new.bis between von and bis ;
        if ln_anzahl > 0 then
        raise_application_error (-20001, 'Zeitraum überschneidet
        sich');
        end if ;
        end ;
        (keine ahnung, ob das oben prinzipiell kompilieren würde -
        habe gerade kein oracle zur verfügung...)
        geht mir genauso

        Allerdings: Diese autonomen Transaktionen in Tabellen-Triggern
        habe ich persönlich noch nie angewandt - ein bisserl heikel
        ist es halt schon, den sobald mehrere Records eingefügt
        werden, ist der Zustand der Tabelle gar nicht mehr mal so
        eindeutig definierbar ...

        Grüße,
        Tomh

        • Antwort von nach einem Tag 0 hilfreich
          Re^4: Befehlsorientierter Trigger: Wie Zeiträume v

          Hallo,

          wenn dir alle intelligenten Wege verbaut sind - komische Aufgabe - schreib doch einfach eine Prozedur, die die gesamte Tabelle überprüft. Die stößt du einfach in deinem geliebten Statement Trigger an und wirfst gegebenenfalls eine Exception.

          Gruß

          Peter [Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

          • Antwort von nach einem Tag 0 hilfreich
            Re^5: Befehlsorientierter Trigger: Wie Zeiträume v

            Man soll halt lernen, wie`s geht... :)

            Alles klar, damit versuch ichs jetzt mal... Hallo,

            wenn dir alle intelligenten Wege verbaut sind - komische
            Aufgabe - schreib doch einfach eine Prozedur, die die gesamte
            Tabelle überprüft. Die stößt du einfach in deinem geliebten
            Statement Trigger an und wirfst gegebenenfalls eine Exception.

            Gruß

            Peter

      • Antwort von nach 2 Tagen 0 hilfreich
        Re^3: Befehlsorientierter Trigger: Wie Zeiträume v

        Hallo Tom, ich fürchte, so einfach wird das nicht. das problem ist, dass
        oracle es einem trigger nicht zulässt, auf die tabelle, auf
        die gerade zugegriffen wird, lesend zuzugreifen.
        Doch, doch, das Zauberwort nennt sich "Autonomous Transaction"
        Jetzt bin ich aber enttäuscht: Das funktioniert doch gar nicht!

        SQL> create table mar_tab (von date, bis date);
        Table created.
        SQL> create or replace trigger mar_trg before insert on mar_tab for each row
        2  DECLARE
        3    PRAGMA AUTONOMOUS_TRANSACTION;
        4    ln_anzahl  number ;
        5  begin
        6    select count(*)
        7      into ln_anzahl
        8      from mar_tab
        9     where :new.von between von and bis
        10        or :new.bis between von and bis ;
        11    if ln_anzahl > 0 then
        12      raise_application_error (-20001, 'Zeitraum überschneidet sich');
        13    end if ;
        14  end ;
        15  /
        Trigger created.
        SQL> insert into mar_tab values (sysdate-10, sysdate-5);
        1 row created.

        Jetzt machen wir ein zweites SQL*Plus auf und schreiben dort:
        SQL> insert into mar_tab values (sysdate-7, sysdate-2);
        1 row created.
        SQL> commit;
        Commit complete.
        Wieder zurück im ersten Fenster:
        SQL> commit;
        Commit complete.
        SQL> select * from mar_tab;
        VON        BIS
        ---------- ----------
        11.05.2007 16.05.2007
        14.05.2007 19.05.2007


        Und jetzt? (keine ahnung, ob das oben prinzipiell kompilieren würde -
        habe gerade kein oracle zur verfügung...)
        geht mir genauso
        Tut es, löst aber leider das Problem nicht. Dir fehlt der "Lock table" (der einem übrigens auch ohne autonomous_transaction nicht erspart bleibt).

        autonomous_transaction kann hier aber noch mehr:
        SQL> delete from mar_tab;
        2 rows deleted.
        SQL> commit;
        Commit complete.
        SQL> insert into mar_tab values (sysdate - 5, sysdate);
        1 row created.
        SQL> insert into mar_tab values (sysdate - 3, sysdate - 1);
        1 row created.
        SQL> insert into mar_tab values (sysdate - 2, sysdate - 2);
        1 row created.
        SQL> commit;
        Commit complete.
        SQL> select * from mar_tab;
        VON       BIS
        --------- ---------
        16-MAY-07 21-MAY-07
        19-MAY-07 19-MAY-07
        18-MAY-07 20-MAY-07


        Sieht gar nicht gut aus... Allerdings: Diese autonomen Transaktionen in Tabellen-Triggern
        habe ich persönlich noch nie angewandt - ein bisserl heikel
        ist es halt schon, den sobald mehrere Records eingefügt
        werden, ist der Zustand der Tabelle gar nicht mehr mal so
        eindeutig definierbar ...
        Doch, eindeutig definierbar schon. Das Problem ist, dass mit Triggern referentielle zwischen bzw. logische Integrität innerhalb von Tabellen nur abbildbar ist, wenn da mindestens ein "Lock Table" drinsteht - zumindest solange es keine ON COMMIT Trigger gibt. Dafür waren Trigger natürlich auch nie gedacht.

        So, jetzt habe ich zwar deine Lösung niedergemacht, aber mir selbst fällt natürlich ausser dem Lock auch nichts vernünftiges ein. Allerdings müsste sich da bei http://asktom.oracle.com was finden, ist ja kein Problem, das völlig aus der Welt ist. Mir schwebt da natürlich wieder was mit Function based indices im Kopf herum, ich kann es nur nicht dingfest machen *g*

        Liebe Grüße,
        Martin



Keine passende Antwort gefunden? Jetzt eigene Frage stellen!