Andreas: Trigger SQL Server 2000

Hallo zusammen,

ich habe eine Frage bezüglich Trigger.
Das Problem ist folgendes: Ich habe in einer Datenbank (SQL Server 2000) einen Trigger auf einer Tabelle t. Dieser Trigger soll, nachdem sich eine Spalte in t geändert hat, eine weitere Spalte in einer anderen Tabelle v ändern.

Der Trigger sieht so aus:
if (update(spalte1)) update v set spalte2= (select top 1 spalte1 from inserted) where ......

Ich setze dann ein SQL Statement von einer ASP aus ab: Update t set spalte1 = Wert ....
Das blöde ist jetzt dass sich der Trigger nur auf den ersten Datensatz auswirkt, nicht aber auf mehrere. Erwisch ich also mit dem Update mehrere Zeilen aus Tabelle t, wird nur eine Zeile in Tabelle v geändert.
Umgehen kann ich das Problem indem ich in der ASP zuerst ein Recordset Objekt auf Tabelle t öffne und dann mit einer Schleife die Zeilen einzeln update.
Das ist allerdings leider nicht sehr performant.

Gibt es eine Möglichkeit mit einem Update Statement den Trigger mehrfach auszulösen?

Grüße
Andreas

  1. Hi,

    Der Trigger sieht so aus:
    if (update(spalte1)) update v set spalte2= (select top 1 spalte1 from inserted) where ......

    ich vermute mal, dass der Trigger ein wneig anders aussieht. Erstelle doch mal ein Script fuer diesen Trigger und praesentiere dann den Code hier.

    Ich setze dann ein SQL Statement von einer ASP aus ab: Update t set spalte1 = Wert ....
    Das blöde ist jetzt dass sich der Trigger nur auf den ersten Datensatz auswirkt, nicht aber auf mehrere. Erwisch ich also mit dem Update mehrere Zeilen aus Tabelle t, wird nur eine Zeile in Tabelle v geändert.

    Weil der Trigger etwas anderes macht, als er soll. Siehe oben.

    Gibt es eine Möglichkeit mit einem Update Statement den Trigger mehrfach auszulösen?

    Nein (erst einmal nicht ;-), aber der Trigger hat doch, abhaengig vom Typ, Zugriff auf die gesamte Aenderungsanforderung. Es gibt doch da die temporaeren Tabellen Deleted und Inserted, wenn mich meine Erinnerung nicht taeuscht.

    Gruss,
    Ludger

  2. Hi,

    wenn mehrere Datensätze mit einem SQL Batch (in einer impliziten Transaktion) auf einer Tabelle mit Trigger geändert (insert/update/delete) werden, dann zündet der Trigger auch nur einmal.

    Um dieses Verhalten zu umgehen, musst du den Trigger entsprechend schreiben, mit einem CURSOR versehen.

    DECLARE @spalte1Var varchar(255) -- oder was auch immer du da für einen Datentyp hast
    DECLARE insertCur CURSOR FAST_FORWARD
    FOR SELECT [spalte1] FROM inserted
    OPEN insertCur
    FETCH NEXT FROM insertCur INTO @spalte1Var
    WHILE @@FETCH_STATUS = 0 BEGIN
      UPDATE [v] SET [spalte2] = @spalte1Var
      WHERE ....

    FETCH NEXT FROM insertCur INTO @spalte1Var
    END

    CLOSE insertCur
    DEALLOCATE insertCur

    ... warum verunstaltet "ihr" eure Problemstellungen eigentlich immer mit Pseudobezeichnungen, wenn "ihr" den original-Quellcode postet, fällt unsereinem das Antworten leichter - so ein Code-Fetzen ist doch kein Geschäftsgeheimnis (wer soll ich da weitere Infos von ableiten können)

    ... wozu verwendest du ein SELECT TOP 1 .. da bekommst du sowieso nur einen Datensatz

    HTH; Ciao, Frank

    1. Hi,

      wenn mehrere Datensätze mit einem SQL Batch (in einer impliziten Transaktion) auf einer Tabelle mit Trigger geändert (insert/update/delete) werden, dann zündet der Trigger auch nur einmal.

      Um dieses Verhalten zu umgehen, musst du den Trigger entsprechend schreiben, mit einem CURSOR versehen.

      dass der Trigger einmal ausgefuehrt wird, ist klar. Was meinst Du mit "Verhalten umgehen"?

      Desweiteren habe ich verstanden, dass Du die Inserted durchlaeufst und dann ein Update machst. Dieses mit dem Ziel einen weiteren Trigger auszuloesen? (Sorry, ich habe nicht ganz verstanden, was Du machst.)

      Gruss,
      Ludger

      1. Hi Ludger,

        mit "Verhalten umgehen" meinte ich das Verhalten, was sich da beim Kollegen zeigt: dass nur ein Update in der Triggerausführung gemacht wird, obwohl er vielleicht mehrere Datensätze in der Tabelle [t] (seiner Ausgangstabelle, auf welcher der Trigger liegt) geändert hat.

        Also:

        UPDATE [t] SET [spalte1] = {irgendeine expression} WHERE {irgendeine expression}     -- ändert in einem Rutsch vielleicht 1000 Datensätze

        Der UPDATE Trigger wird aber nur einmal gefeuert, es wird (im Fall des Kollegen) nur einmal von [inserted] gelesen (nämlich der letzte Datensatz in [inserted] mit SELECT [spalte1] FROM [inserted]   (das TOP 1 mal weggelassen)

        [inserted] beinhaltet aber mehrere Datensätze. An all diese da drinnen kommst du nur mit einem Cursor über [inserted]. Innerhalb des Cursors mache ich ein Update auf [v] (eine weitere Tabelle, nicht die originale mit dem Trigger).

        Ob er auf [v] einen Trigger hat, der wiederum [t] ändert weiß ich nicht. Wäre dann ein rekursives Trigger-Verhalten, was man für gewöhnlich meiden sollte (deaktivieren sollte).

        Mir ist nur nicht ganze klar warum er SELECT TOP 1 [spalte1] FROM [inserted] benutzt - das ORDER BY nicht zu vergessen - da er damit von der ganzen Tabelle auch nur einen einzigen Datensatz bekommt, den ersten anhand einer spezifischen Sortierung eben.

        ... dieses Trigger-Problem ist recht weit verbreitet. :-)

        Ciao, als denn
        Frank

        1. Hi,

          ... dieses Trigger-Problem ist recht weit verbreitet. :-)

          aha, das "Trigger-Konzept" wurde nicht verstanden und es wird beklagt, dass der Trigger nur einmal auegeloest wird und scheinbar nur einen Datensatz betrifft und - logische Schlussfolgerung - die Forderung wird laut, dass ein und derselbe Trigger doch n fach ausgeloest werden muss.
          Dazu noch etwas "TOP 1"-Problematik.

          Ich danke Dir fuer die Erlaeuterungen.

          Gruss,
          Ludger

          1. Hi,

            tolles Resumé  .... schreibst du ne Kolumne?  ;)

            Als denn, Frank

    2. ... warum verunstaltet "ihr" eure Problemstellungen eigentlich immer mit Pseudobezeichnungen, wenn "ihr" den original-Quellcode postet, fällt unsereinem das Antworten leichter - so ein Code-Fetzen ist doch kein Geschäftsgeheimnis (wer soll ich da weitere Infos von ableiten können)

      ... wozu verwendest du ein SELECT TOP 1 .. da bekommst du sowieso nur einen Datensatz

      Vielen Dank für den Tipp!
      Von wegen Pseudocode: Du hast natürlich Recht, dass das kein Geschäftsgeheimnis ist. Andererseits versuche ich immmer, den Code so zu vereinfachen, dass wirklich nur das nötigste drinsteht. Aber das ist wohl Geschmacksache. Wegen dem Top 1: In der Unterabfrage darf sowieso nur ein Wert zurückgegeben werden, deswegen schadet top 1 nicht.

      Grüße Andreas

      1. Hi,

        Wegen dem Top 1: In der Unterabfrage darf sowieso nur ein Wert zurückgegeben werden, deswegen schadet top 1 nicht.

        doch es schadet, wenn auf diese Art und Weise eine (hilfreiche) Fehlermeldung eben nicht hochkommt. Ausserdem schadet es, weil Du nicht mehr weisst was passiert, die Bearbeitung des Codes also nicht mehr deterministisch erfolgt, wenn ich das mal so schreiben darf.

        Gruss,
        Ludger

      2. Hi,

        ... nur das deine Vereinfachung eher zu einer Totalverklärung geführt hat

        Bilden wir doch mal ein Beispiel ab:

        [t] beinhaltet ca. 500 Datensätze
        in ASP:    UPDATE [t] SET [spalte1] = 'Wert'

        • alle 500 Records in [t] bekommen in [spalte1] den Wert 'Wert'
        • dein UpdateTrigger wird einmal gefeuert, die Updateprüfung auf [spalte1] bringt das Trigger SQL Statement zur Ausführung
        • [inserted] beinhaltet 500 Datensätze
        • dein UPDATE [v] SET [spalte2] = (SELECT TOP 1 [spalte1] from [inserted]) WHERE ...
            setzt alle Datensätze in [v].[spalte2] auf den obersten (weil TOP x bedingt eine Sortierung) Wert aus der Liste der 500 upgedateten Datensätze in [inserted], die einer bestimmten Bedingung entsprechen
        • von deiner Beschreibung gehe ich mal davon aus, dass du auf einen Eintrag in [inserted] filterst, also (SELECT TOP 1 [spalteX] FROM [inserted])
        • lässt du das TOP 1 weg, würde SELECT bis zum Ende von [inserted] lesen (imho ... das würde ich jetzt nicht beschreien wollen)
        • das Ergebnis, dass du beschreibst, ist klar. Du führst ja das UPDATE nur _1x_ für einen (Bedingung) Datensatz in [v] aus, möchtest aber, dass sich [v] bei allen mit [t] verwandten, geänderten Datensätzen ändert?

        Und genau dafür gibt es den CURSOR, den man dann im Trigger verwenden sollte. Du selektierst dann von [inserted] genau 2 Spalten, einmal deine [spalte1] und dann die Spalte, welche die Verbindung zu [v] ist. (Von mir aus auch weitere Spalten aus [inserted], je nachdem was du für das Update-Statement brauchst.)

        Zur Definition von Triggern siehe SQL BOL (SQL Server Books Online) = SQL Online Hilfe :-)

        Ciao, als denn.
        Frank