{"id":55000431,"date":"2007-02-01T00:00:00","date_gmt":"2021-02-11T21:08:00","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=431"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Trigger_im_SQL_Server_","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Trigger_im_SQL_Server_\/","title":{"rendered":"Trigger im SQL Server"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg03.met.vgwort.de\/na\/9b5a1aa86864483e9363fc7c0e3d5015\" width=\"1\" height=\"1\" alt=\"\">        <\/p>\n<p><b>Auch wenn Access und die Jet-Engine bereits zahlreiche M&ouml;glichkeiten einer &#8222;richtigen&#8220; Datenbank enthalten, fehlen immer noch wichtige Funktionen: So finden Sie etwa beim Umstieg auf SQL Server-Backends viel Bekanntes, aber auch Unbekanntes &#8211; zum Beispiel Trigger. Im vorliegenden Beitrag stellen wir den Nutzen von und den Umgang mit Triggern vor.<\/b><\/p>\n<p>Welche Aufgaben haben Trigger im SQL Server eigentlich Laut &uuml;bersetzungshilfe beschreibt das Substantiv &#8222;Trigger&#8220; den Ausl&ouml;ser, den Ansto&szlig; oder den Abzugsb&uuml;gel und das Verb entsprechend ansteuern, ausl&ouml;sen, einleiten.<\/p>\n<p>Ein Trigger l&ouml;st also bei bestimmten Aktionen etwas aus. Da wir uns im SQL Server befinden, kann also nur ein SQL-Befehl etwas ausl&ouml;sen und das kann hier nat&uuml;rlich auch nur wieder ein SQL-Statement sein. Damit realisieren Trigger etwa die Funktionalit&auml;t, die wir vom Client und dabei etwa aus Access als VBA-Ereignisprozedur kennen. Mit Ereignisprozeduren k&ouml;nnen Sie beispielsweise beim Einf&uuml;gen eines Datensatzes eine Aktion ausl&ouml;sen und diesen Vorgang unter bestimmten Bedingungen abbrechen (s. Listing 1).<\/p>\n<table>\n<tbody>\n<tr>\n<td>\n<p class=\"tabellenkopf\">Listing 1: Eine Ereignisprozedur in VBA<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p class=\"quellcode-funktionssammlung-1-5\">Private Sub Form_BeforeInsert(Cancel As Integer)<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">     If Name = &quot;Doofmann&quot; Then<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">     MsgBox &quot;Solche Namen wollen wir hier nicht!&quot;<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">     Cancel = True<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">     End If<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">End Sub<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Die unterschiedlichen Client-Programmierumgebungen unterst&uuml;tzen unterschiedliche Ereignisse. Was in VBA unter Access geht, ist vielleicht unter ADO.NET nicht verf&uuml;gbar, und andersherum, unter ADO und C++ wiederum finden Sie wieder ganz andere M&ouml;glichkeiten.<\/p>\n<p>Wenn aber die unterschiedlichen Clients solche Funktionen kennen, warum brauchen wir sie dann noch einmal im SQL Server In einer richtigen Client-Server-Umgebung ist es eben nicht sichergestellt, dass alle Ver&auml;nderungen an den Daten einer Datenbank &uuml;ber denselben Client abgewickelt werden. Wenn Sie eine Access-Anwendung auf einer SQL Server 2005-Datenbank programmieren und vor dem L&ouml;schen eines Datensatzes diesen unbedingt in eine &#8222;Ist-gel&ouml;scht-worden&#8220;-Tabelle schreiben m&ouml;chten, dann ist die Ereignisprozedur in Access nicht der richtige Weg, da jeder Berechtigte direkt auf die SQL Server-Datenbank zugreifen und ein <b>DELETE FROM tblMeineDaten WHERE ID=9 <\/b>absetzen kann. Der Datensatz w&auml;re ohne Dokumentation verschwunden (die Syntax &#8222;DELETE * FROM&#8230;&#8220;, also mit &#8222;*&#8220; ist im &uuml;brigen Access-Dialekt und nicht ANSI SQL).<\/p>\n<p>Die Funktionalit&auml;t wird also vom Client auf den Server verschoben. &auml;hnliches kennen Sie schon von der referentiellen Integrit&auml;t. Auch das L&ouml;schen von Master-Datens&auml;tzen bei existierenden Detail-Datens&auml;tzen kann man mit einer client-seitigen Ereignisprozedur &uuml;berwachen und gegebenenfalls verhindern, doch Access kann dies auch automatisch unterlassen.<\/p>\n<p>Im SQL Server haben Sie noch viel mehr M&ouml;glichkeiten: Angenommen, Sie m&ouml;chten die erw&auml;hnte Funktionalit&auml;t umsetzen und jeden Datensatz vor dem L&ouml;schen in eine Dokumentations- oder Historisierungstabelle schreiben, dann sind Trigger genau das Richtige f&uuml;r Sie.<\/p>\n<p>Zum Nachvollziehen der Beispiele bauen Sie am besten direkt eine kleine Testdatenbank auf. Dazu k&ouml;nnen Sie das Skript aus Listing 2 verwenden, das Sie am einfachsten &uuml;ber das SQL Server Management Studio des SQL Servers 2005 eingeben und ausgef&uuml;hren (weitere Informationen siehe [1]). Die Syntax wird aber auch vom SQL Server 2000 unterst&uuml;tzt, die ersten Beispiele k&ouml;nnen Sie daher auch auf diesem nachvollziehen und das Skript beispielsweise im Query Analyser des SQL Servers 2000 ausf&uuml;hren. Wenn Sie die Beispiele wieder l&ouml;schen wollen, l&ouml;schen Sie einfach die gesamte Test-Datenbank mit dem Befehl <b>DROP DATABASE TestTrigger<\/b>. Falls Sie die Fehlermeldung erhalten, dass die Datenbank nicht gel&ouml;scht werden kann, weil sie in Benutzung ist (wahrscheinlich von Ihnen selbst durch die Tests), hilft der folgende kleine Trick beim Schlie&szlig;en aller bestehenden Verbindungen:<\/p>\n<pre>USE master\r\ngo\r\nALTER DATABASE TriggerTest SET SINGLE_USER WITH ROLLBACK IMMEDIATE\r\ngo\r\nDROP DATABASE TriggerTest<\/pre>\n<p>Das Skript aus Listing 2 erledigt Folgendes: Der Befehl <b>USE<\/b> wechselt den aktuellen Datenbankkontext, weil es im SQL Server in einer Instanz mehrere Datenbanken geben kann (und meistens auch gibt). Nach dem Erstellen der Datenbank <b>TestTrigger <\/b>erstellt das Skript mit der <b>CREATE TABLE<\/b>-Anweisung zwei Tabellen. Die erste Tabelle <b>MyData <\/b>enth&auml;lt die Ausgangsdaten, die zweite Tabelle <b>MyDataHistory<\/b> soll die L&ouml;schvorg&auml;nge mit Uhrzeit (<b>DEFAULT <\/b>entspricht dem Standardwert eines Feldes unter Access) und aktuellem Benutzer dokumentieren. Der Befehl <b>GO <\/b>beendet einen Batch, wobei zur Vereinfachung beinahe alle Befehle in einem einzigen Batch untergebracht sind. Die Anweisungen k&ouml;nnen dann einzeln markiert und nacheinander ausgef&uuml;hrt werden (wenn Sie <b>F5<\/b> oder <b>STRG+E <\/b>dr&uuml;cken, wird sowohl im Query Analyser des SQL Server 2000 als auch im SQL Server Management Studio des SQL Server 2005 nur die Markierung ausgef&uuml;hrt).<\/p>\n<table>\n<tbody>\n<tr>\n<td>\n<p class=\"tabellenkopf\">Listing 2: Erstellen der ben&ouml;tigten Objekte f&uuml;r die Beispiele<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre>-- SETUP der Beispieldatenbank ----------<\/pre>\n<pre>USE master<\/pre>\n<pre>go<\/pre>\n<pre>CREATE DATABASE TriggerTest<\/pre>\n<pre>GO<\/pre>\n<pre>USE TriggerTest<\/pre>\n<pre>GO<\/pre>\n<pre>CREATE TABLE MyData<\/pre>\n<pre>    ID int identity Primary Key,<\/pre>\n<pre>    Name nvarchar(80)<\/pre>\n<pre>)<\/pre>\n<pre>GO<\/pre>\n<pre>INSERT INTO MyData VALUES (&apos;M&uuml;ller&apos;)<\/pre>\n<pre>INSERT INTO MyData VALUES (&apos;Maier&apos;)<\/pre>\n<pre>INSERT INTO MyData VALUES (&apos;Schmidt&apos;)<\/pre>\n<pre>INSERT INTO MyData VALUES (&apos;Schulz&apos;)<\/pre>\n<pre>INSERT INTO MyData VALUES (&apos;Sch&auml;fer&apos;)<\/pre>\n<pre>GO<\/pre>\n<pre>SELECT * FROM myData<\/pre>\n<pre>GO<\/pre>\n<pre>CREATE TABLE MyDataHistory<\/pre>\n<pre>    ID int,<\/pre>\n<pre>    Name nvarchar(80),<\/pre>\n<pre>    DeletedAt datetime DEFAULT GETDATE(),<\/pre>\n<pre>    DeletedFrom nvarchar(80) DEFAULT SYSTEM_USER<\/pre>\n<pre>)<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><b>Ein einfacher Trigger<\/b><\/p>\n<p>Einen Trigger erstellt man wie andere Objekte in SQL mit dem <b>CREATE<\/b>-Befehl, hier mit <b>CREATE TRIGGER<\/b>. Trigger wie der geplante werden immer f&uuml;r eine bestimmte Tabelle erstellt. Diese Tabelle legt man mit dem Schl&uuml;sselwort <b>ON <\/b>fest (s. Listing 3).<\/p>\n<table>\n<tbody>\n<tr>\n<td>\n<p class=\"tabellenkopf\">Listing 3: Erstellen eines einfachen Triggers<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p class=\"quellcode-funktionssammlung-1-5\">CREATE TRIGGER DataHistory ON myData FOR DELETE<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">    AS<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">BEGIN<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">     INSERT INTO MyDataHistory (ID, Name) SELECT ID, Name FROM deleted<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">END<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Beachten Sie das Schl&uuml;sselwort <b>FOR DELETE<\/b>. Genauso k&ouml;nnen Sie auch Trigger f&uuml;r das Einf&uuml;gen und &auml;ndern erstellen, dann verwenden Sie das Schl&uuml;sselwort <b>FOR INSERT <\/b>oder <b>FOR UPDATE<\/b>. Es gibt auch gleichnamige gemeinsame Trigger.<\/p>\n<p>L&ouml;schen Sie nun mit den folgenden Anweisungen einmal ein paar Datens&auml;tze:<\/p>\n<pre>DELETE FROM MyData where ID=2\r\nDELETE FROM MyData where ID=5\r\nGO<\/pre>\n<p>Schauen Sie sich das Ergebnis in den beiden Tabellen an:<\/p>\n<pre>SELECT * FROM myData\r\nSELECT * FROM myDataHistory<\/pre>\n<p>Interessant ist hier der Befehl, der im Trigger selbst ausgef&uuml;hrt wird: <b>INSERT INTO&#8230; SELECT &#8230; FROM deleted<\/b>. Um welche Tabelle handelt es sich denn bei <b>deleted<\/b><\/p>\n<p>Dies ist keine tats&auml;chlich im SQL Server vorhandene Tabelle. Die Abfrage <b>SELECT * FROM deleted <\/b>wird vom SQL Server dementsprechend mit der Fehlermeldung <b>Invalid object name &apos;deleted&#8220;<\/b> beantwortet.<\/p>\n<p><b>Die Trigger-Tabellen<\/b><\/p>\n<p>Der SQL Server stellt die Tabelle <b>deleted<\/b> allein im Kontext eines Triggers bereit und auch nur f&uuml;r die Dauer der Durchf&uuml;hrung des Triggers. Es handelt sich tats&auml;chlich um eine Tabelle, wenn auch keine im &uuml;blichen Sinne, und nicht etwa um einen einzelnen Datensatz, denn in einem SQL-Statement k&ouml;nnen durchaus mehrere Datens&auml;tze gel&ouml;scht werden. F&uuml;r jedes Statement wird der Trigger aber nur einmal ausgel&ouml;st. Wenn Sie drei Datens&auml;tze wie im unten stehenden Listing l&ouml;schen, wird der Trigger trotzdem nur einmal ausgel&ouml;st, die Tabelle <b>deleted <\/b>enth&auml;lt dann eben drei Datens&auml;tze, wie wir einfach nachweisen k&ouml;nnen.<\/p>\n<p>Bemerkenswert ist, dass der SQL Server beim L&ouml;schen von Datens&auml;tzen auch zwei Meldungen erzeugt (siehe Bild 1). <b>(3 row(s) affected) <\/b>bezieht sich bei der ersten Meldung auf das Einf&uuml;gen in die History-Tabelle und bei der zweiten Meldung auf den L&ouml;schvorgang in <b>MyData<\/b>. Ob dies tats&auml;chlich die richtige Reihenfolge ist, l&auml;sst sich leicht pr&uuml;fen. Dazu &auml;ndern Sie den Trigger wie folgt, was dazu f&uuml;hrt, dass der Datensatz mit dem Wert <b>Beethoven <\/b>f&uuml;r das Feld <b>Name<\/b> nicht in die History-Tabelle geschrieben wird.<\/p>\n<pre>ALTER TRIGGER [dbo].[DataHistory] ON [dbo].[MyData] FOR DELETE\r\nAS\r\nBEGIN\r\nINSERT INTO MyDataHistory (ID, NAME)\r\nSELECT ID, Name FROM deleted WHERE Name&lt;&gt;&apos;Beethoven&apos;\r\nEND<\/pre>\n<p>            <img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2007_01\/Trigger-web-images\/Trigger01_opt.jpeg\" alt=\"Trigger01.bmp\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 1: Ausgabe im SQL Server Management Studio<\/span><\/b><\/p>\n<p><!--30percent--><\/p>\n<p>Dementsprechend sollten nun beim L&ouml;schen dreier Datens&auml;tze zwei Meldungen erscheinen, von denen die eine drei und die andere zwei Datens&auml;tze betrifft. Wie Bild 2 zeigt, f&uuml;hrt der SQL Server tats&auml;chlich zun&auml;chst die im Trigger enthaltenen Operationen und erst dann den eigentlichen L&ouml;schvorgang durch.<\/p>\n<p>            <img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2007_01\/Trigger-web-images\/Trigger02_opt.jpeg\" alt=\"Trigger02.bmp\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 2: Der Trigger f&uuml;gt erst die gel&ouml;schten Datens&auml;tze ein und l&ouml;scht diese dann aus der Originaltabelle.<\/span><\/b><\/p>\n<p>So jedenfalls scheint es. Die Aktion des Triggers wird aber laut Dokumentation erst nach der eigentlichen Aktion des SQL-Statements ausgef&uuml;hrt. Alle Trigger im SQL Server 2000 und 2005 sind standardm&auml;&szlig;ig AFTER-Trigger. Das Schl&uuml;sselwort FOR ist dabei gleichbedeutend mit AFTER. <\/p>\n<p><b>INSERT- und UPDATE-Trigger<\/b><\/p>\n<p>Wir haben bislang immer mit einem DELETE-Trigger experimentiert, wie verhalten sich die anderen DML (Data Manipulation Language)-Trigger, die beim Einf&uuml;gen (<b>Insert<\/b>) und Aktualisieren (<b>Update<\/b>) ausgef&uuml;hrt werden<\/p>\n<p>Im Prinzip genauso, nur werden unterschiedliche Tabellen innerhalb der Trigger vom SQL Server verwaltet. Um dies nachzuvollziehen, erweitern wir die History-Tabelle um einen Eintrag, der anzeigt, ob die Daten gel&ouml;scht, eingef&uuml;gt oder ge&auml;ndert wurden (s. Listing 4).<\/p>\n<table>\n<tbody>\n<tr>\n<td>\n<p class=\"tabellenkopf\">Listing 4: Historisieren eingef&uuml;gter Datens&auml;tze<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre>ALTER TABLE MyDataHistory ADD Type nvarchar(8)<\/pre>\n<pre>GO<\/pre>\n<pre>UPDATE MyDataHistory SET Type=&apos;delete&apos;<\/pre>\n<pre>GO<\/pre>\n<pre>ALTER TRIGGER dbo.DataHistory ON dbo.MyData FOR DELETE<\/pre>\n<pre>AS<\/pre>\n<pre>BEGIN<\/pre>\n<pre>     INSERT INTO MyDataHistory (ID, Name, Type)<\/pre>\n<pre>     Select ID, Name, &apos;delete&apos; FROM deleted <\/pre>\n<pre>END<\/pre>\n<pre>GO<\/pre>\n<pre>CREATE TRIGGER dbo.InsertHistory ON dbo.MyData AFTER INSERT<\/pre>\n<pre>AS<\/pre>\n<pre>BEGIN<\/pre>\n<pre>    INSERT INTO MyDataHistory (ID, Name, Type) <\/pre>\n<pre>    Select ID, Name, &apos;insert&apos; FROM inserted <\/pre>\n<pre>END<\/pre>\n<pre>GO<\/pre>\n<pre>INSERT INTO MyData VALUES (&apos;Wagner&apos;)<\/pre>\n<pre>GO<\/pre>\n<pre>SELECT * FROM MyData<\/pre>\n<pre>SELECT * FROM MyDataHistory<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Wie Sie sehen, wird beim Einf&uuml;gen innerhalb des INSERT-Triggers eine Tabelle <b>inserted <\/b>bereitgestellt. Und beim UPDATE-Trigger Falsch geraten! Es gibt keine Tabelle namens <b>updated<\/b>. Stattdessen benutzt der Update-Trigger beide von den anderen Triggern verwendeten Tabellen. In der Tabelle <b>inserted <\/b>werden die neuen Werte abgelegt und in der Tabelle <b>deleted <\/b>die gel&ouml;schten (s. Listing 5).<\/p>\n<table>\n<tbody>\n<tr>\n<td>\n<p class=\"tabellenkopf\">Listing 5: Update-Trigger greifen auf die beiden Tabellen deleted und inserted zu.<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre>CREATE TRIGGER dbo.UpdateHistory ON dbo.MyData AFTER UPDATE<\/pre>\n<pre>AS<\/pre>\n<pre>BEGIN<\/pre>\n<pre>     INSERT INTO MyDataHistory (ID, Name, Type) <\/pre>\n<pre>     SELECT inserted.ID, <\/pre>\n<pre>     isnull(deleted.Name,&apos;(NULL)&apos;)+&apos;-&gt;&apos;+isnull(inserted.<\/pre>\n<pre>     name,&apos;(NULL)&apos;), &apos;update&apos; <\/pre>\n<pre>     FROM inserted inner join deleted on<\/pre>\n<pre>     inserted.ID=deleted.ID<\/pre>\n<pre>END<\/pre>\n<\/td>\n<\/tr>\n<tr>\n<td>\n                        <\/td>\n<\/tr>\n<tr>\n<td>\n                        <\/td>\n<\/tr>\n<tr>\n<td>\n                        <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Ein kleiner Tipp: Viele clientseitige Zugriffstechnologien wie ADO und ADO.NET k&ouml;nnen Probleme mit den Meldungen der Trigger und allen anderen Statusmeldungen bekommen. Wie Sie im Screenshot aus Bild 3 sehen, w&uuml;rden Sie zu Recht ein g&uuml;ltiges Recordset erwarten. Durch die L&ouml;schmeldung aber ist es nicht mehr m&ouml;glich, auf dieses zuzugreifen.<\/p>\n<p>            <img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2007_01\/Trigger-web-images\/Trigger03_opt.jpeg\" alt=\"Trigger03.bmp\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 3: ADO und L&ouml;schmeldungen<\/span><\/b><\/p>\n<p>Sie k&ouml;nnen daher im obigen Beispiel den <b>Open<\/b>-Befehl durch den folgenden ersetzen, dann funktioniert auch wieder der Zugriff auf das Recordset:<\/p>\n<pre>SET NOCOUNT ON;\r\nDELETE FROM MyData WHERE Name LIKE &apos;B%&apos;\r\nSELECT * FROM MyData<\/pre>\n<p>Der Befehl <b>SET NOCOUNT ON <\/b>sorgt daf&uuml;r, dass keine Statusmeldungen &uuml;ber die Anzahl der vom L&ouml;schen, Einf&uuml;gen oder &auml;ndern betroffenen Datens&auml;tze mehr vom SQL Server gesendet werden:<\/p>\n<pre>SET NOCOUNT ON\r\nINSERT INTO MyData VALUES (&apos;Bach&apos;)\r\nGO\r\nDELETE FROM MYData WHERE Name=&apos;Bach&apos;<\/pre>\n<p><b>Transaktionen und Trigger<\/b><\/p>\n<p>Wie aber verh&auml;lt es sich mit der M&ouml;glichkeit, SQL-Aktionen in einem Trigger abzubrechen Im ersten Beispiel, der Ereignisprozedur in VBA, war dies m&ouml;glich &#8211; aber genauso einfach geht es auch im Trigger. Dazu muss man das Transaktionshandling des SQL Servers ein bisschen genauer verstehen. Eine Transaktion ist, wie Sie sicher wissen, ein Satz von SQL-Anweisungen, der entweder vollst&auml;ndig oder gar nicht ausgef&uuml;hrt wird. Daher ist auch jeder einzelne SQL-Befehl eine Transaktion, denn Sie w&uuml;rden sicher zu Recht davon ausgehen, dass bei der Anweisung<\/p>\n<pre>UPDATE Employee SET Gehalt=Gehalt*1.1<\/pre>\n<p>entweder alle Angestellten eine Gehaltserh&ouml;hung bekommen oder keiner. Eine solche Transaktion wird im SQL Server &#8222;implizit&#8220; genannt. Eine explizite Transaktion wird mit dem Schl&uuml;sselwort <b>BEGIN TRAN <\/b>oder <b>BEGIN TRANSACTION <\/b>eingef&uuml;hrt und dann mit <b>COMMIT TRAN <\/b>best&auml;tigt oder mit <b>ROLLBACK TRAN <\/b>abgebrochen.<\/p>\n<p>Alternativ k&ouml;nnen Sie nat&uuml;rlich auch clientseitige Methoden verwenden: etwa die <b>BeginTrans<\/b>&#8211; oder <b>RollbackTrans<\/b>&#8211; und <b>CommitTrans<\/b>-Methode des <b>ADODB.Connection<\/b>-Objektes oder den <b>TransactionContext <\/b>von ADO.NET. Folgendes Listing zeigt ein Beispiel f&uuml;r eine explizite Transaktion:<\/p>\n<pre>SET NOCOUNT OFF\r\nGO\r\nBEGIN TRANSACTION\r\n UPDATE MyData Set Name=&apos;Haydn&apos; WHERE ID=1\r\n UPDATE MyData Set Name=&apos;Gluck&apos; WHERE ID=2\r\nIF @@ROWCOUNT =0 ROLLBACK TRAN ELSE COMMIT TRAN\r\nGO\r\nSELECT * FROM MyData<\/pre>\n<p>Im Listing wird die Transaktion &#8222;zur&uuml;ckgerollt&#8220;, wenn die letzte Anweisung keinen Datensatz betrifft (<b>@@ROWCOUNT=0<\/b>), was hier der Fall ist. Da die gesamte Transaktion verworfen wird, ist also auch der Datensatz mit ID <b>1<\/b> und dem Namen <b>M&uuml;ller <\/b>noch vorhanden.<\/p>\n<p>Wann aber wird der Trigger aufgerufen Vor, nach oder w&auml;hrend der Transaktion &auml;ndern wir den Trigger so, dass er eine Meldung ausgibt, wenn er aufgerufen wird (siehe Bild 4).<\/p>\n<p>            <img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2007_01\/Trigger-web-images\/Trigger04_opt.jpeg\" alt=\"Trigger04.bmp\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 4: Der Trigger sorgt f&uuml;r die Ausgabe einer Meldung.<\/span><\/b><\/p>\n<p>Auch wenn kein Datensatz wirklich gel&ouml;scht wurde, wird der Trigger durch den DELETE-Befehl ausgel&ouml;st und f&uuml;gt 0 Datens&auml;tze in die History-Tabelle ein.<\/p>\n<p>Bestimmte Aspekte einer Datenbank beziehungsweise einer Tabelle werden vom SQL Server &uuml;berpr&uuml;ft, bevor eine Transaktion ausgef&uuml;hrt wird. Dazu geh&ouml;ren beispielsweise die Einschr&auml;nkungen auf Tabellenebene. So kennt der SQL Server etwa die Foreign-Key-Einschr&auml;nkung. Das Skript aus Listing 6 erstellt eine weitere Tabelle namens <b>MyDataDetail <\/b>und f&uuml;gt Datens&auml;tze ein, die &uuml;ber den Fremdschl&uuml;ssel der ID des Komponisten miteinander verbunden sind. Interessant ist es, nun ein <b>DELETE<\/b>-Statement auszuf&uuml;hren, das versucht, einen Datensatz aus <b>MyData <\/b>(also der &#8222;Komponisten&#8220;-Tabelle) zu l&ouml;schen, f&uuml;r den Detaildatens&auml;tze vorhanden sind (ID 14 nat&uuml;rlich, hier &#8222;Mozart&#8220;):<\/p>\n<pre>GO\r\nDELETE FROM MyData Where Name=&apos;Mozart&apos;<\/pre>\n<table>\n<tbody>\n<tr>\n<td>\n<p class=\"tabellenkopf\">Listing 6: Erstellen einer 1:n-Relation<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre>UPDATE MyData Set Name=&apos;Haydn&apos; WHERE ID=1<\/pre>\n<pre>UPDATE MyData Set Name=&apos;Gluck&apos; WHERE ID=3<\/pre>\n<pre>UPDATE MyData Set Name=&apos;Brahms&apos; WHERE ID=4<\/pre>\n<pre>INSERT INTO MyData VALUES (&apos;Bach&apos;)<\/pre>\n<pre>INSERT INTO MyData VALUES (&apos;Mozart&apos;)<\/pre>\n<pre>INSERT INTO MyData VALUES (&apos;Beethoven&apos;)<\/pre>\n<pre>GO<\/pre>\n<pre>SELECT * FROM MyData<\/pre>\n<pre>GO<\/pre>\n<pre>CREATE TABLE MyDataDetail<\/pre>\n<pre>    ID int identity PRIMARY KEY,<\/pre>\n<pre>    MasterID int,<\/pre>\n<pre>    Opus varchar(180)<\/pre>\n<pre>)<\/pre>\n<pre>GO<\/pre>\n<pre>ALTER TABLE dbo.MyDataDetail WITH CHECK ADD CONSTRAINT FK_MasterData FOREIGN KEY(MasterID)<\/pre>\n<pre>REFERENCES dbo.MyData (ID)<\/pre>\n<pre>GO<\/pre>\n<pre>INSERT INTO MyDataDetail VALUES (14,&apos;KV 550 1788 Symphonie Nr. 40 g-moll&apos;)<\/pre>\n<pre>INSERT INTO MyDataDetail VALUES (14,&apos;KV 551 1788 Symphonie Nr. 41 &apos;&apos;Jupiter&apos;&apos; C-Dur&apos;)<\/pre>\n<pre>INSERT INTO MyDataDetail VALUES (14,&apos;KV 552 1788 Klavierlied &apos;&apos;Beim Auszug in das Feld, ...&apos;)<\/pre>\n<pre>GO<\/pre>\n<pre>DELETE FROM MyData Where Name=&apos;Mozart&apos;<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Der SQL Server antwortet mit einer Fehlermeldung, da die Fremdschl&uuml;ssel-Einschr&auml;nkung referentielle Integrit&auml;t definiert, ohne L&ouml;schweitergabe zu konfigurieren &#8211; auch dies ist im SQL Server seit Version 2000 m&ouml;glich. Dann jedoch h&auml;tte das <b>FOREIGN KEY CONSTRAINT <\/b>mit der Option <b>ON DELETE CASCADE <\/b>erstellt werden m&uuml;ssen.<\/p>\n<p>Wenn Sie das <b>DELETE<\/b>-Statement ausf&uuml;hren, werden Sie allerdings beobachten, dass zwar die Fehlermeldung ausgegeben wird, aber keine Ausgabe aus dem Trigger erfolgt. Der SQL Server beachtet n&auml;mlich alle <b>CONSTRANTS<\/b>, bevor er eine Transaktion ausf&uuml;hrt.<\/p>\n<p>Tats&auml;chlich wird der Trigger innerhalb der Transaktion aufgerufen, das hei&szlig;t, dass man eine solche Transaktion tats&auml;chlich auch im Trigger abbrechen kann. Dazu &auml;ndern Sie den bestehenden Trigger und verhindern, dass ein Datensatz mit dem Namen &#8222;Brahms&#8220; gel&ouml;scht wird (siehe Bild 5).<\/p>\n<p>            <img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2007_01\/Trigger-web-images\/Trigger05_opt.jpeg\" alt=\"Trigger05.bmp\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 5: Der Datensatz mit dem Namen &#8222;Brahms&#8220; wird nun nicht mehr gel&ouml;scht.<\/span><\/b><\/p>\n<p>Einen anderen Datensatz k&ouml;nnen sie etwa mit folgender Anweisung problemlos l&ouml;schen:<\/p>\n<pre>DELETE FROM MyData WHERE Name=&apos;Haydn&apos;<\/pre>\n<p><b>Vorsicht bei Trigger-Transaktionen<\/b><\/p>\n<p>Da Trigger innerhalb der Transaktionen aufgerufen werden, sind sie entsprechend vorsichtig einzusetzen. Beachten Sie, dass die Transaktion umso l&auml;nger dauert, je mehr Aktionen innerhalb von Triggern ausgef&uuml;hrt werden. Gerade bei stark belasteten Datenbanken und Servern sind Trigger daher ein gef&auml;hrlicher Performance-Engpass, da sie unweigerlich bei jeder Transaktion, f&uuml;r die sie definiert sind, aufgerufen werden. Besonders abzuraten ist es daher, im Trigger Anweisungen auszuf&uuml;hren, die auf Daten oder Objekte au&szlig;erhalb des SQL Servers zugreifen, also etwa &uuml;ber Systemprozeduren E-Mails zu verschicken oder &auml;hnliches. Im neuen SQL Server 2005 k&ouml;nnte man in solchen Szenarien eine Service Broker Nachricht verschicken, die ein anderes System dar&uuml;ber informiert, dass eine E-Mail zu versenden sei, um so den Trigger und das Versenden der E-Mail zu entkoppeln.<\/p>\n<p><b>Trigger an- und abschalten<\/b><\/p>\n<p>Oftmals ist es daher w&uuml;nschenswert, dass die Trigger zwar w&auml;hrend der normalen Dateneingabe aktiv, aber bei Massenimporten wegen der starken Leistungseinschr&auml;nkung abschaltbar sind. Zudem kann man beim Import die vom Trigger ausgef&uuml;hrten Aktionen mit beachten und gleich ausf&uuml;hren. Daher besteht die M&ouml;glichkeit, einen Trigger auch tempor&auml;r aus- und dann nat&uuml;rlich auch wieder anzuschalten. Dies geschieht mit einem der folgenden Befehle:<\/p>\n<pre>DISABLE TRIGGER triggername \r\nENABLE TRIGGER triggername.<\/pre>\n<p class=\"zwischen-berschrift-oberer-spaltenrand\">Instead of Trigger<\/p>\n<p>Eine vielfach unbekannte M&ouml;glichkeit der Trigger ist es, nicht aktualisierbare Views (deutsch: Sichten, das abgespeckte Pendant zu Access-Abfragen, deren Funktion im SQL Server meist nur mit gespeicherten Prozeduren realisiert werden kann), dennoch aktualisierbar zu machen.<\/p>\n<p>Das folgende Listing erstellt eine View mit einem Inner Join, die die Namen der Komponisten mit ihren Werken ausgibt. Nat&uuml;rlich kann eine solche View normalerweise nicht aktualisiert werden, aber im Beispiel wird ein Einf&uuml;gen erm&ouml;glicht. Das L&ouml;schen und &auml;ndern m&uuml;sste man noch entsprechend implementieren.<\/p>\n<p>Daher erkl&auml;rt sich auch der Name dieser Trigger: Statt wirklich in die Tabellen einzuf&uuml;gen und den Trigger im Transaktionskontext aufzurufen, wird allein der Trigger angesto&szlig;en. Innerhalb des Beispiel-Triggers wird versucht zu ermitteln, ob es den Komponisten schon in der Tabelle <b>MyData <\/b>gibt. In <b>@masterID <\/b>ist nach dem ersten <b>SELECT <\/b>entweder die <b>ID <\/b>des Namens oder eben <b>NULL<\/b> gespeichert. Falls der Name nicht gefunden wird, wird er &uuml;ber einen <b>INSERT<\/b>-Befehl eingef&uuml;gt, danach die ID des soeben eingef&uuml;gten Namens ermittelt.<\/p>\n<p>Benutzen Sie in solchen Zusammenh&auml;ngen immer die Funktion <b>IDENT_CURRENT(&apos;NameDerTabelle&#8220;)<\/b>. Es gibt im SQL Server eine System-Variable namens <b>@@IDENTITY<\/b>, die den zuletzt eingef&uuml;gten ID-Wert ausgibt. Ich habe einmal ein halben Tag damit verbracht, zu verstehen, warum dieser Wert immer ein v&ouml;llig anderer war als der der Tabelle, in die ich gerade einf&uuml;gte. Die L&ouml;sung war schlie&szlig;lich ganz einfach: Ein Trigger f&uuml;gte munter auch in andere Tabellen noch Datens&auml;tze ein (&auml;hnlich unserem History-Trigger), deren <b>ID <\/b>lieferte mir dann <b>@@IDENTITY<\/b>. <b>IDENT_CURRENT <\/b>gibt jedoch immer genau den letzten Identit&auml;tswert der angegebenen Tabelle aus.<\/p>\n<p><b>Nachteile von Triggern<\/b><\/p>\n<p>Dies deutet auf einen anderen negativen Aspekt von Triggern hin: Sie sind schwer zu entdecken und schwer zu verwalten. Ein <b>INSERT <\/b>kann durch einen Trigger v&ouml;llig unerwartete Resultate erhalten, wenn man die Datenbank nicht selbst geschrieben hat oder eine wirklich gute Dokumentation vorliegt, was nur sehr selten der Fall ist. Es sei noch angemerkt, dass die M&ouml;glichkeiten von <b>Instead of<\/b>-Triggern sich nicht im Behandeln von Views ersch&ouml;pfen. Auch auf Tabellen k&ouml;nnen sie angewendet werden und so alle m&ouml;glichen Funktionen implementieren.<\/p>\n<p><b>SQL Server 2005<\/b><\/p>\n<p>Mit dem SQL Server 2005 wurden zudem so genannte <b>DDL <\/b>(<b>Data Definition Language<\/b>)-Trigger eingef&uuml;hrt &#8211; Trigger, die auf die &auml;nderung der Struktur der Daten reagieren. Der folgende Trigger macht es unm&ouml;glich, Tabellen zu &auml;ndern oder zu l&ouml;schen, bevor er nicht abgeschaltet oder gel&ouml;scht wird.<\/p>\n<pre>CREATE TRIGGER safety \r\nON DATABASE \r\nFOR DROP_TABLE, ALTER_TABLE \r\nAS \r\n PRINT &apos;You must disable Trigger &quot;safety&quot; \r\n to drop or alter tables!&apos; \r\n ROLLBACK;<\/pre>\n<p><b>Zusammenfassung<\/b><\/p>\n<p>Mit Triggern kann man, wie mit den Ereignisprozeduren, die wir vom Client kennen, auf SQL-Ereignisse reagieren. Trigger stellen die Tabellen <b>inserted <\/b>und <b>deleted <\/b>bereit, die es m&ouml;glich machen, die Werte, mit denen das Statement ausf&uuml;hrt werden soll, zu erkennen.<\/p>\n<p>Dabei wird der Trigger innerhalb der Transaktion ausgef&uuml;hrt &#8211; entgegen den Einschr&auml;nkungen (constraints), die immer vor einer solchen Transaktion beachtet werden. Daher kann die Transaktion auch innerhalb des Triggers mit ROLLBACK TRAN abgebrochen werden. Deshalb ist gro&szlig;e Vorsicht geboten, wenn es um die Programmierung von Triggern geht. Alle Trigger-Aktionen sollten so kurz und schnell wie irgend m&ouml;glich erstellt werden, um Performance-Probleme zu umgehen.<\/p>\n<p>Trigger sind f&uuml;r den Client v&ouml;llig transparent. Man kann daher auch bei einer funktionierenden und laufenden Anwendung Trigger implementieren, die neue Funktionalit&auml;ten mitbringen, wie etwa das Dokumentieren von &auml;nderungen, ohne die bestehenden Anwendungen zu &auml;ndern. Diese Transparenz macht ihre Wartung teilweise jedoch auch schwierig und aufw&auml;ndig. Nicht immer ist sofort zu erkennen, welche Ph&auml;nomene in einer Datenbank von Triggern hervorgerufen werden.<\/p>\n<p class=\"zwischen-berschriftquellen\">Quellen<\/p>\n<p class=\"quellen\">[1] Das SQL Server Management Studio Express, Ausgabe 6\/2006, Shortlink 410<\/p>\n<table>\n<tbody>\n<tr>\n<td>\n<p class=\"tabellenkopf\">Listing 7: Beispiel f&uuml;r einen Instead-of-Trigger<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<pre>CREATE VIEW dbo.MyKompData AS<\/pre>\n<pre>SELECT d.ID,d.Name,dd.Opus FROM MyData d INNER JOIN MyDataDetail dd ON d.ID=dd.MasterID<\/pre>\n<pre>GO<\/pre>\n<pre>CREATE TRIGGER meinInsteadOfTrigger ON dbo.MyKompData INSTEAD OF INSERT<\/pre>\n<pre>AS<\/pre>\n<pre>BEGIN<\/pre>\n<pre>     declare @masterID int<\/pre>\n<pre>     SELECT @masterID=mydata.ID FROM MyData<\/pre>\n<pre>     INNER JOIN inserted ON MyData.Name=inserted.Name <\/pre>\n<pre>     IF @masterID is NULL begin<\/pre>\n<pre>     INSERT INTO MyData SELECT Name FROM inserted<\/pre>\n<pre>     SET @masterID=IDENT_CURRENT(&apos;MyData&apos;)<\/pre>\n<pre>     END<\/pre>\n<pre>     INSERT INTO MyDataDetail (masterID, opus)<\/pre>\n<pre>SELECT @masterID,Opus FROM inserted<\/pre>\n<pre>END<\/pre>\n<pre>GO<\/pre>\n<pre>INSERT INTO MyKompData (Name, opus)<\/pre>\n<pre>VALUES (&apos;Vivaldi&apos;,&apos;Die vier Jahreszeiten&apos;)<\/pre>\n<pre>GO<\/pre>\n<pre>SELECT * FROM MyData<\/pre>\n<pre>GO<\/pre>\n<pre>SELECT * FROM MyKompData<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>Downloads zu diesem Beitrag<\/h3>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>Trigger.sql<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/77490BBA-EA03-4A17-85BE-BE172A203797\/aiu_431.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Auch wenn Access und die Jet-Engine bereits zahlreiche M&ouml;glichkeiten einer &#8222;richtigen&#8220; Datenbank enthalten, fehlen immer noch wichtige Funktionen: So finden Sie etwa beim Umstieg auf SQL Server-Backends viel Bekanntes, aber auch Unbekanntes &#8211; zum Beispiel Trigger. Im vorliegenden Beitrag stellen wir den Nutzen von und den Umgang mit Triggern vor.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[66012007,662007,44000022],"tags":[],"class_list":["post-55000431","post","type-post","status-publish","format-standard","hentry","category-66012007","category-662007","category-SQL_Server_und_Co"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.9 (Yoast SEO v27.4) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Trigger im SQL Server  - Access im Unternehmen<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/access-im-unternehmen.de\/Trigger_im_SQL_Server_\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Trigger im SQL Server\" \/>\n<meta property=\"og:description\" content=\"Auch wenn Access und die Jet-Engine bereits zahlreiche M&ouml;glichkeiten einer &quot;richtigen&quot; Datenbank enthalten, fehlen immer noch wichtige Funktionen: So finden Sie etwa beim Umstieg auf SQL Server-Backends viel Bekanntes, aber auch Unbekanntes - zum Beispiel Trigger. Im vorliegenden Beitrag stellen wir den Nutzen von und den Umgang mit Triggern vor.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Trigger_im_SQL_Server_\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2021-02-11T21:08:00+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg03.met.vgwort.de\/na\/9b5a1aa86864483e9363fc7c0e3d5015\" \/>\n<meta name=\"author\" content=\"Andr\u00e9 Minhorst\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Andr\u00e9 Minhorst\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"19\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Trigger_im_SQL_Server_\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Trigger_im_SQL_Server_\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Trigger im SQL Server\",\"datePublished\":\"2021-02-11T21:08:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Trigger_im_SQL_Server_\\\/\"},\"wordCount\":3070,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Trigger_im_SQL_Server_\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg03.met.vgwort.de\\\/na\\\/9b5a1aa86864483e9363fc7c0e3d5015\",\"articleSection\":[\"1\\\/2007\",\"2007\",\"SQL Server und Co.\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Trigger_im_SQL_Server_\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Trigger_im_SQL_Server_\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Trigger_im_SQL_Server_\\\/\",\"name\":\"Trigger im SQL Server - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Trigger_im_SQL_Server_\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Trigger_im_SQL_Server_\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg03.met.vgwort.de\\\/na\\\/9b5a1aa86864483e9363fc7c0e3d5015\",\"datePublished\":\"2021-02-11T21:08:00+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Trigger_im_SQL_Server_\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Trigger_im_SQL_Server_\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Trigger_im_SQL_Server_\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg03.met.vgwort.de\\\/na\\\/9b5a1aa86864483e9363fc7c0e3d5015\",\"contentUrl\":\"http:\\\/\\\/vg03.met.vgwort.de\\\/na\\\/9b5a1aa86864483e9363fc7c0e3d5015\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Trigger_im_SQL_Server_\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Trigger im SQL Server\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"name\":\"Access im Unternehmen\",\"description\":\"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access\",\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/access-im-unternehmen.de\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\",\"name\":\"Andr\u00e9 Minhorst Verlag\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"contentUrl\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"width\":370,\"height\":111,\"caption\":\"Andr\u00e9 Minhorst Verlag\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\",\"name\":\"Andr\u00e9 Minhorst\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"caption\":\"Andr\u00e9 Minhorst\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Trigger im SQL Server  - Access im Unternehmen","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/access-im-unternehmen.de\/Trigger_im_SQL_Server_\/","og_locale":"de_DE","og_type":"article","og_title":"Trigger im SQL Server","og_description":"Auch wenn Access und die Jet-Engine bereits zahlreiche M&ouml;glichkeiten einer \"richtigen\" Datenbank enthalten, fehlen immer noch wichtige Funktionen: So finden Sie etwa beim Umstieg auf SQL Server-Backends viel Bekanntes, aber auch Unbekanntes - zum Beispiel Trigger. Im vorliegenden Beitrag stellen wir den Nutzen von und den Umgang mit Triggern vor.","og_url":"https:\/\/access-im-unternehmen.de\/Trigger_im_SQL_Server_\/","og_site_name":"Access im Unternehmen","article_published_time":"2021-02-11T21:08:00+00:00","og_image":[{"url":"http:\/\/vg03.met.vgwort.de\/na\/9b5a1aa86864483e9363fc7c0e3d5015","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"19\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/Trigger_im_SQL_Server_\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Trigger_im_SQL_Server_\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Trigger im SQL Server","datePublished":"2021-02-11T21:08:00+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Trigger_im_SQL_Server_\/"},"wordCount":3070,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Trigger_im_SQL_Server_\/#primaryimage"},"thumbnailUrl":"http:\/\/vg03.met.vgwort.de\/na\/9b5a1aa86864483e9363fc7c0e3d5015","articleSection":["1\/2007","2007","SQL Server und Co."],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Trigger_im_SQL_Server_\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Trigger_im_SQL_Server_\/","url":"https:\/\/access-im-unternehmen.de\/Trigger_im_SQL_Server_\/","name":"Trigger im SQL Server - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Trigger_im_SQL_Server_\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Trigger_im_SQL_Server_\/#primaryimage"},"thumbnailUrl":"http:\/\/vg03.met.vgwort.de\/na\/9b5a1aa86864483e9363fc7c0e3d5015","datePublished":"2021-02-11T21:08:00+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Trigger_im_SQL_Server_\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Trigger_im_SQL_Server_\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/Trigger_im_SQL_Server_\/#primaryimage","url":"http:\/\/vg03.met.vgwort.de\/na\/9b5a1aa86864483e9363fc7c0e3d5015","contentUrl":"http:\/\/vg03.met.vgwort.de\/na\/9b5a1aa86864483e9363fc7c0e3d5015"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Trigger_im_SQL_Server_\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Trigger im SQL Server"}]},{"@type":"WebSite","@id":"https:\/\/access-im-unternehmen.de\/#website","url":"https:\/\/access-im-unternehmen.de\/","name":"Access im Unternehmen","description":"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access","publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/access-im-unternehmen.de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/access-im-unternehmen.de\/#organization","name":"Andr\u00e9 Minhorst Verlag","url":"https:\/\/access-im-unternehmen.de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/","url":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","contentUrl":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","width":370,"height":111,"caption":"Andr\u00e9 Minhorst Verlag"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f","name":"Andr\u00e9 Minhorst","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","caption":"Andr\u00e9 Minhorst"}}]}},"_links":{"self":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55000431","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/comments?post=55000431"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55000431\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55000431"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55000431"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55000431"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}