{"id":719,"date":"2007-09-17T18:40:45","date_gmt":"2007-09-17T16:40:45","guid":{"rendered":"http:\/\/www.glorf.it\/blog\/2007\/09\/17\/sql-talk\/identity-werte-und-trigger"},"modified":"2007-09-17T18:41:59","modified_gmt":"2007-09-17T16:41:59","slug":"identity-werte-und-trigger","status":"publish","type":"post","link":"http:\/\/www.glorf.it\/blog\/2007\/09\/17\/sql-talk\/identity-werte-und-trigger","title":{"rendered":"Identity-Werte und Trigger"},"content":{"rendered":"<p>Wenn man mit Identity-Werten arbeitet, dann kann man ganz bequem &#252;ber die Funktionen @@IDENTITY und SCOPE_IDENTITY() den zuletzt vergebenen Wert erfragen. Wenn man mit Triggern arbeitet, dann kann man auf dabei auf unerwartete Schwierigkeiten sto&#223;en. Wird in dem Trigger beispielsweise in einer Protokoll-Tabelle ein Eintrag gemacht, dann liefern die beiden genannten Funktionen unterschiedliche Werte zur&#252;ck:<\/p>\n<ul>\n<li>@@IDENTITY liefert den zuletzt vergebenen Identity-Wert zur&#252;ck. In diesem Fall die ID der Protokoll-Tabelle.<\/li>\n<li>SCOPE_IDENTITY() liefert hingegen die ID aus der \u201erichtigen\u201c Tabelle. <\/li>\n<\/ul>\n<p>Das ist ziemlich einleuchtend, kann aber noch komplizierter werden, wenn man mit Instead-Of-Triggern arbeitet. F&#252;r unser Beispiel stelle ich mit eine View vor, die in deren Instead-Of-Trigger die Aktion auf die Basistabelle weiterleitet, also INSERT, UPDATE oder DELETE und dann einen Eintrag in eine Protokoll-Tabelle macht.<br \/>\nIn dieser fall sind die Ergebnisse der Funktionen anders:<\/p>\n<ul>\n<li>@@IDENTITY liefert den zuletzt vergebenen Identity-Wert zur&#252;ck. In diesem Fall die ID der Protokoll-Tabelle. Falls auf der Tabelle ein Trigger liegt, dann diese, sonst der aus dem Trigger der View.<\/li>\n<li>SCOPE_IDENTITY() liefet hingegen NULL, weil in dem Kontext ja gar kein INSERT durchgef&#252;hrt wurde, sondern nur im Instead-Of-Trigger. <\/li>\n<li>Will man sich mit der OUTPUT-Klausel behelfen, dann wird f&#252;r inserted.ID immer der Wert 0 ausgegeben. <\/li>\n<\/ul>\n<p>Die Schl&#252;sse daraus mag nun jeder selber ziehen. Ich f&#252;r meinen Teil lasse die Finger lieber von Triggern und setze statt dessen Stored-Procedures ein.<\/p>\n<p>Anbei ein paar Samples zum selber ausprobieren:<\/p>\n<p><code lang=\"sql\">if object_id ('protokoll') IS NOT NULL<br \/>\n\tdrop table protokoll<br \/>\ngo<br \/>\nif object_id (&#x0027;protokoll&#x0027;) IS NULL<br \/>\ncreate table protokoll (<br \/>\n\tid\tinteger\t identity(1,1) NOT null primary key nonclustered,<br \/>\n\tts\tdatetime\tNOT NULL default getdate(),<br \/>\n\tspid\tinteger NOT NULL default @@SPID,<br \/>\n\t[user]\tsysname NOT NULL default user,<br \/>\n\tpid\t\tinteger NULL,<br \/>\n\tuid\t\tinteger NULL,<br \/>\n\tstmt varchar(100) NOT NULL,<br \/>\n\tcomment varchar(300) NULL)<br \/>\ngo<\/p>\n<p>if object_id (&#x0027;MyTriggTab&#x0027;) IS NOT NULL<br \/>\n\tDROP TABLE MyTriggTab<br \/>\ngo<br \/>\nCREATE TABLE MyTriggTab (<br \/>\n\tid\tinteger\t identity(1,1) NOT null primary key nonclustered,<br \/>\n\tcomment varchar(300) NULL)<br \/>\ngo<br \/>\nCREATE TRIGGER T1 ON MyTriggTab<br \/>\nAFTER INSERT<br \/>\nAS<br \/>\n\tINSERT INTO protokoll (stmt, pid, uid)<br \/>\n\t\tSELECT &#x0027;AFTER INSERT&#x0027;, @@SPID, user_id()<br \/>\ngo<br \/>\nCREATE TRIGGER T2 ON MyTriggTab<br \/>\nAFTER UPDATE<br \/>\nAS<br \/>\n\tINSERT INTO protokoll (stmt, pid, uid)<br \/>\n\t\tSELECT &#x0027;AFTER UPDATE&#x0027;, @@SPID, user_id()<br \/>\ngo<br \/>\nCREATE TRIGGER T3 ON MyTriggTab<br \/>\nAFTER DELETE<br \/>\nAS<br \/>\n\tINSERT INTO protokoll (stmt, pid, uid)<br \/>\n\t\tSELECT &#x0027;AFTER DELETE&#x0027;, @@SPID, user_id()<br \/>\ngo<br \/>\nINSERT INTO MyTriggTab(comment) VALUES (&#x0027;Test&#x0027;);<br \/>\nSELECT\t&#x0027;INSERT INTO MyTriggTab&#x0027; as &quot;Statement&quot;,<br \/>\n\t\tSCOPE_IDENTITY() as &quot;SCOPE_IDENTITY()&quot;,\t&#8211;> liefert null<br \/>\n\t\t@@IDENTITY\t\t as &quot;@@IDENTITY&quot;;\t\t&#8211;> liefert ID aus Protokoll-Tabelle<\/p>\n<p>UPDATE MyTriggTab set comment = &#x0027;Hello&#x0027;;<br \/>\nSELECT\t&#x0027;UPDATE MyTriggTab&#x0027; as &quot;Statement&quot;,<br \/>\n\t\tSCOPE_IDENTITY() as &quot;SCOPE_IDENTITY()&quot;,\t&#8211;> liefert null<br \/>\n\t\t@@IDENTITY\t\t as &quot;@@IDENTITY&quot;;\t\t&#8211;> liefert ID aus Protokoll-Tabelle<\/p>\n<p>DELETE MyTriggTab WHERE ID = SCOPE_IDENTITY();<br \/>\nSELECT\t&#x0027;DELETE FROM MyTriggTab&#x0027; as &quot;Statement&quot;,<br \/>\n\t\tSCOPE_IDENTITY() as &quot;SCOPE_IDENTITY()&quot;,\t&#8211;> liefert null<br \/>\n\t\t@@IDENTITY\t\t as &quot;@@IDENTITY&quot;;\t\t&#8211;> liefert ID aus Protokoll-Tabelle<br \/>\ngo<br \/>\nIF object_id (&#x0027;MyTriggView&#x0027;) IS NOT NULL<br \/>\n\tDROP VIEW MyTriggView<br \/>\ngo<br \/>\nCREATE VIEW MyTriggView (ID, info)<br \/>\nAS<br \/>\nSELECT TOP(3) ID, comment<br \/>\n\tFROM MyTriggTab<br \/>\n\tORDER BY ID DESC<br \/>\ngo<br \/>\nCREATE TRIGGER T4 ON MyTriggView<br \/>\nINSTEAD OF INSERT<br \/>\nAS<br \/>\n\tINSERT INTO protokoll (stmt, pid, uid)<br \/>\n\t\tSELECT &#x0027;INSTEAD OF INSERT&#x0027;, @@SPID, user_id()<br \/>\n\tINSERT INTO MyTriggTab(comment)<br \/>\n\t\tSELECT inserted.info<br \/>\n\t\t\tFROM inserted<br \/>\ngo<br \/>\nCREATE TRIGGER T5 ON MyTriggView<br \/>\nINSTEAD OF UPDATE<br \/>\nAS<br \/>\n\tINSERT INTO protokoll (stmt, pid, uid)<br \/>\n\t\tSELECT &#x0027;INSTEAD OF UPDATE&#x0027;, @@SPID, user_id()<br \/>\n\tUPDATE MyTriggTab<br \/>\n\t\tset comment = inserted.info<br \/>\n\tFROM MyTriggTab JOIN inserted ON MyTriggTab.ID=inserted.ID;<br \/>\ngo<br \/>\nCREATE TRIGGER T6 ON MyTriggView<br \/>\nINSTEAD OF DELETE<br \/>\nAS<br \/>\n\tINSERT INTO protokoll (stmt, pid, uid)<br \/>\n\t\tSELECT &#x0027;INSTEAD OF DELETE&#x0027;, @@SPID, user_id()<br \/>\n\tDELETE FROM MyTriggTab<br \/>\n\t\tFROM MyTriggTab JOIN deleted ON MyTriggTab.ID=deleted.ID;<br \/>\ngo<br \/>\nINSERT INTO MyTriggView(info) VALUES (&#x0027;View-Test&#x0027;);<br \/>\nSELECT\t&#x0027;INSERT INTO MyTriggView&#x0027; as &quot;Statement&quot;,<br \/>\n\t\tSCOPE_IDENTITY() as &quot;SCOPE_IDENTITY()&quot;,\t&#8211;> liefert null<br \/>\n\t\t@@IDENTITY\t\t as &quot;@@IDENTITY&quot;;\t\t&#8211;> liefert ID aus Protokoll-Tabelle<\/p>\n<p>UPDATE MyTriggView set info = &#x0027;Hallo&#x0027;;<br \/>\nSELECT\t&#x0027;UPDATE MyTriggView&#x0027; as &quot;Statement&quot;,<br \/>\n\t\tSCOPE_IDENTITY() as &quot;SCOPE_IDENTITY()&quot;,\t&#8211;> liefert null<br \/>\n\t\t@@IDENTITY\t\t as &quot;@@IDENTITY&quot;;\t\t&#8211;> liefert ID aus Protokoll-Tabelle<\/p>\n<p>DELETE MyTriggView WHERE ID = (SELECT MIN(ID) FROM MyTriggView);<br \/>\nSELECT\t&#x0027;DELETE FROM MyTriggView&#x0027; as &quot;Statement&quot;,<br \/>\n\t\tSCOPE_IDENTITY() as &quot;SCOPE_IDENTITY()&quot;,\t&#8211;> liefert null<br \/>\n\t\t@@IDENTITY\t\t as &quot;@@IDENTITY&quot;;\t\t&#8211;> liefert ID aus Protokoll-Tabelle<br \/>\ngo<br \/>\nDECLARE @outtable TABLE(ID integer);<\/p>\n<p>INSERT INTO MyTriggView(info)<br \/>\n\tOUTPUT inserted.ID INTO @outtable<br \/>\n\tVALUES (&#x0027;View-Test&#x0027;);<br \/>\nSELECT\t&#x0027;INSERT INTO MyTriggView&#x0027; as &quot;Statement&quot;,<br \/>\n\t\tID\t\t\t\t as &quot;OUTPUT&quot;,<br \/>\n\t\tSCOPE_IDENTITY() as &quot;SCOPE_IDENTITY()&quot;,\t&#8211;> liefert null<br \/>\n\t\t@@IDENTITY\t\t as &quot;@@IDENTITY&quot;\t\t&#8211;> liefert ID aus Protokoll-Tabelle<br \/>\n\t\tFROM @outtable<br \/>\ngo<br \/>\nSELECT * FROM MyTriggView;<br \/>\nSELECT * FROM MyTriggTab<br \/>\nSELECT * FROM protokoll<\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wenn man mit Identity-Werten arbeitet, dann kann man ganz bequem &#252;ber die Funktionen @@IDENTITY und SCOPE_IDENTITY() den zuletzt vergebenen Wert erfragen. Wenn man mit Triggern arbeitet, dann kann man auf dabei auf unerwartete Schwierigkeiten sto&#223;en. Wird in dem Trigger beispielsweise in einer Protokoll-Tabelle ein Eintrag gemacht, dann liefern die beiden genannten Funktionen unterschiedliche Werte zur&#252;ck: [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[3],"tags":[],"_links":{"self":[{"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/posts\/719"}],"collection":[{"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/comments?post=719"}],"version-history":[{"count":0,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/posts\/719\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/media?parent=719"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/categories?post=719"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.glorf.it\/blog\/wp-json\/wp\/v2\/tags?post=719"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}