none
Trigger und Tabelle 'Inserted' RRS feed

  • Frage

  • Hallo

    Ich möchte eine History für eine meiner Tabellen machen. Nach jedem hinzufügen eines Datensatzes sollte er mir gewisse Daten in Meine History-Tabelle schreiben. Soweit ich verstanden habe wäre da ein Insert-Trigger eine gute Variante

    Also habe ich mir folgendes gebastelt:

    CREATE TRIGGER tgrWriteHistory
    ON dbo.tblakte
    AFTER INSERT AS
    INSERT INTO dbo.tblHistory (HistoryChange,  PrimaryKeyNr, NameSubForm,  FieldNameShow, OldValue, NewValue, FieldControlType, 
    FieldControlColumn, OldFieldBoundColumn, NewFieldBoundColumn, InsertDateTime, NameUser, NameWorkstation)
    values
    (1, inserted.pAkte, 'frmAkte', 'MainSubject',  0, inserted.MainSubject, 109, 0 , 0, 0, GETDATE(), 'xyz', 'pc'),
    (1, inserted.pAkte, 'frmAkte', 'CreationPeriodFrom', 0, inserted.CreationPeriodFrom, 109, 0, 0, 0, GETDATE(), 'xyz', 'pc')
    
    GO

    Mit meinen Grundkentnissen erwarte ich, dass 'Inserted' mir den Verweis auf den Datensatz gibt, der gerade angefügt wurde und ich somit die einzelnen Felder von dort auslesen kann.

    Wenn ich aber den Trigger kreieren möchte kommt immer die Fehlermeldung mit #4104 'The multi-part identifier "inserted.pAkte" could not be bound', 'The multi-part identifier "inserted.MainSubject" could not be bound.' für alle inserted...-Felder.

    Die Erklärungen zu dem Fehler bringen mich nicht weiter.

    Was mache ich falsch?


    Danke und Gruss Thomas

    Mittwoch, 30. Oktober 2019 18:00

Antworten

  • Hallo Thomas,

    Du brauchst zwei Inserts oder ein UNION ALL für die Selects.

    alter TRIGGER tgrWriteHistory
    ON dbo.tblakte
    AFTER INSERT AS
    
    INSERT INTO dbo.tblHistory (HistoryChange,  PrimaryKeyNr, NameSubForm,  FieldNameShow, OldValue, NewValue, FieldControlType, 
    FieldControlColumn, OldFieldBoundColumn, NewFieldBoundColumn, InsertDateTime, NameUser, NameWorkstation)
    select 1, pAkte, 'frmAkte', 'MainSubject',  0, MainSubject, 109, 0 , 0, 0, GETDATE(), 'xyz', 'pc'  
    	from inserted;
    
    INSERT INTO dbo.tblHistory (HistoryChange,  PrimaryKeyNr, NameSubForm,  FieldNameShow, OldValue, NewValue, FieldControlType, 
    FieldControlColumn, OldFieldBoundColumn, NewFieldBoundColumn, InsertDateTime, NameUser, NameWorkstation)
    select 1, pAkte, 'frmAkte', 'CreationPeriodFrom', 0, CreationPeriodFrom, 109, 0, 0, 0, GETDATE(), 'xyz', 'pc' 
    from inserted;
    GO


    Einen schönen Tag noch, Christoph - http://www.insidesql.org/blogs/cmu

    • Als Antwort markiert Alphawolfi Freitag, 1. November 2019 10:58
    Donnerstag, 31. Oktober 2019 13:50
    Beantworter

Alle Antworten

  • Leider falsch gedacht.
    Da der Trigger je Statement und nicht je Zeile aufgerufen wird enthält die Tabelle Inserted alle hinzugefügten Zeile in Kopie.
    Du muss also per Cursor mittels "Select * from Inserted" die Daten kopieren.
    Dies geht natürlich auch in einem SQL:

    CREATE TRIGGER tgrWriteHistory
    ON dbo.tblakte
    AFTER INSERT AS
    INSERT INTO dbo.tblHistory (HistoryChange,  PrimaryKeyNr, NameSubForm,  FieldNameShow, OldValue, NewValue, FieldControlType, 
    FieldControlColumn, OldFieldBoundColumn, NewFieldBoundColumn, InsertDateTime, NameUser, NameWorkstation)
    select ....
    from Inserted
    GO

    Die "...." ersetzt du natürlich durch die benötigten Felder.

    Der Afterinsert-Trigger ist vor allem bei Bulkinserts schädlich, da die Daten komplett noch mal in die TempDB für den Trigger geschrieben werden.
    Sollte man dabei auch noch auf die Idee kommen, die Daten verändern zu wollen, wird auch noch das Satzversioning scharf gemacht, was zusätzlich die TempDB belastet.

    Alternativ kann man dies nur per Instead-of-Trigger lösen.

    Mittwoch, 30. Oktober 2019 18:53
  • Ok, danke. Ja, ich resp. der User fügt ja immer nur eine Zeile aufs mal an. Das ist somit kein Problem.

    Aber heisst das ich kann auch nur eine Zeile anfügen? Oder wie mache ich das für mehere Zeilen?


    Danke und Gruss Thomas

    Donnerstag, 31. Oktober 2019 09:49
  • Für den Trigger ist das unerheblich ob nur 1 Zeile oder n Zeilen eingefügt wurden.
    Das Prinzip ist immer dasselbe. Die Tabelle "Inserted" enthält dann halt nur 1 Zeile.

    Mehrere Zeilen kann man durch mehrere Values erreichen:

    insert into mytable [(F1, ....)]
    Values
    (.....)
    (.....)
    ;

    insert into mytable [(F1, ...)]
    Select F1, ... from othertable where ...

    Der Trigger wird aber nur 1 Mal aufgerufen auch wenn 2 oder mehr Zeilen eingefügt wurden.


    • Bearbeitet Der Suchende Donnerstag, 31. Oktober 2019 09:55
    Donnerstag, 31. Oktober 2019 09:54
  • Mit dem Value schlüsselwort ist mir klar dass ich mehrere Wert einfügen kann.

    Aber wie Du mir eben erklärt hast muss ich für mein Vorhaben eine Select-Anweisung angeben.

    Mein Trigger sieht momentan so aus:

    alter TRIGGER tgrWriteHistory
    ON dbo.tblakte
    AFTER INSERT AS
    INSERT INTO dbo.tblHistory (HistoryChange,  PrimaryKeyNr, NameSubForm,  FieldNameShow, OldValue, NewValue, FieldControlType, 
    FieldControlColumn, OldFieldBoundColumn, NewFieldBoundColumn, InsertDateTime, NameUser, NameWorkstation)
    
    (select 1, pAkte, 'frmAkte', 'MainSubject',  0, MainSubject, 109, 0 , 0, 0, GETDATE(), 'xyz', 'pc'  from inserted)
    (select 1, pAkte, 'frmAkte', 'CreationPeriodFrom', 0, CreationPeriodFrom, 109, 0, 0, 0, GETDATE(), 'xyz', 'pc' from inserted)
    GO
    Jetzt konnt ich ihn immerhin speichern, aber er führt im Test nur die erste Anweisung (die mit dem MainSubject) aus, die zweite ignoriert er


    Danke und Gruss Thomas

    Donnerstag, 31. Oktober 2019 11:36
  • Hallo Thomas,

    Du brauchst zwei Inserts oder ein UNION ALL für die Selects.

    alter TRIGGER tgrWriteHistory
    ON dbo.tblakte
    AFTER INSERT AS
    
    INSERT INTO dbo.tblHistory (HistoryChange,  PrimaryKeyNr, NameSubForm,  FieldNameShow, OldValue, NewValue, FieldControlType, 
    FieldControlColumn, OldFieldBoundColumn, NewFieldBoundColumn, InsertDateTime, NameUser, NameWorkstation)
    select 1, pAkte, 'frmAkte', 'MainSubject',  0, MainSubject, 109, 0 , 0, 0, GETDATE(), 'xyz', 'pc'  
    	from inserted;
    
    INSERT INTO dbo.tblHistory (HistoryChange,  PrimaryKeyNr, NameSubForm,  FieldNameShow, OldValue, NewValue, FieldControlType, 
    FieldControlColumn, OldFieldBoundColumn, NewFieldBoundColumn, InsertDateTime, NameUser, NameWorkstation)
    select 1, pAkte, 'frmAkte', 'CreationPeriodFrom', 0, CreationPeriodFrom, 109, 0, 0, 0, GETDATE(), 'xyz', 'pc' 
    from inserted;
    GO


    Einen schönen Tag noch, Christoph - http://www.insidesql.org/blogs/cmu

    • Als Antwort markiert Alphawolfi Freitag, 1. November 2019 10:58
    Donnerstag, 31. Oktober 2019 13:50
    Beantworter
  • Ein Select liefert ein Resultset.
    In Klammern wäre das ein scalarer Subselect, der nur 1 Zeile und 1 Wert liefern darf.
    Daher wähle am Besten den Union all:

    INSERT INTO dbo.tblHistory (HistoryChange, PrimaryKeyNr, NameSubForm, FieldNameShow, OldValue, NewValue, FieldControlType, FieldControlColumn, OldFieldBoundColumn, NewFieldBoundColumn, InsertDateTime, NameUser, NameWorkstation) select 1, pAkte, 'frmAkte', 'MainSubject', 0, MainSubject, 109, 0 , 0, 0, GETDATE(), 'xyz', 'pc' from inserted
    union all

    select 1, pAkte, 'frmAkte', 'CreationPeriodFrom', 0, CreationPeriodFrom, 109, 0, 0, 0, GETDATE(), 'xyz', 'pc' from inserted



    • Bearbeitet Der Suchende Donnerstag, 31. Oktober 2019 18:55
    Donnerstag, 31. Oktober 2019 18:54
  • Danke für Eure Antworten. Das hilft mir weiter.



    Danke und Gruss Thomas

    Freitag, 1. November 2019 10:57