{"id":55001211,"date":"2019-12-01T00:00:00","date_gmt":"2020-07-10T09:43:56","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1211"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Datenhistorie_per_Trigger","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Datenhistorie_per_Trigger\/","title":{"rendered":"Datenhistorie per Trigger"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg05.met.vgwort.de\/na\/7fa82b1cfa8d44f6a84ff62172c03ba0\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>Wer seine Access-Tabellen von einem Access-Backend in ein SQL Server-Backend &uuml;bertragen hat, d&uuml;rfte zun&auml;chst keinen Unterschied beim Zugriff auf die Daten bemerken. Spannend wird es, wenn Sie unter Access jedoch die sogenannten Datenmakros verwendet haben, um automatisch auf &Auml;nderungen in den Daten zu reagieren und beispielsweise Kopien ge&auml;nderter oder gel&ouml;schter Datens&auml;tze in einer Archivtabelle angelegt haben. Beim Migrieren nach SQL Server werden zwar auch die Archivtabellen erstellt, aber die Datenmakros bleiben au&szlig;en vor. Damit keine Daten verloren gehen, zeigen wir in diesem Beitrag, wie Sie die Tabellen mit Triggern ausstatten, um die gew&uuml;nschten Daten zu archivieren.<\/b><\/p>\n<p><b>Voraussetzungen<\/b><\/p>\n<p>F&uuml;r den Zugriff auf den SQL Server und speziell f&uuml;r das &Auml;ndern des Datenmodells, zu dem auch die Trigger geh&ouml;ren, kann man das SQL Server Management Studio verwenden. Wir nutzen zwar gern die Tools, die wir im Beitrag <b>SQL Server-Tools <\/b>vorgestellt haben (siehe <b>www.access-im-unternehmen.de\/1061<\/b>) &#8211; aber im Rahmen des vorliegenden Beitrags wollen wir die Aufgabe zun&auml;chst im SQL Server Management Studio erledigen. Sp&auml;ter erfahren Sie, wie wir dann doch noch unsere Tools zum Anlegen von Triggern und Archivtabellen nutzen k&ouml;nnen.<\/p>\n<p><b>Ziel des Beitrags<\/b><\/p>\n<p>Das Ziel ist, f&uuml;r die Tabellen, f&uuml;r die es notwendig ist, Trigger zu definieren, die beim L&ouml;schen oder &Auml;ndern von Datens&auml;tzen einer Tabelle die zuletzt verwendete Version des jeweiligen Datensatzes zu archivieren. Wenn Sie also einen Datensatz l&ouml;schen, soll dieser vor dem L&ouml;schen in eine als Archivtabelle angelegte Kopie der Tabelle mit dem Originaldatensatz kopiert werden. Wenn ein Datensatz ge&auml;ndert wird, soll ebenfalls eine Kopie des Originaldatensatzes in der Archivtabelle gespeichert werden.<\/p>\n<p>In beiden F&auml;llen stellt sich die Frage: Welche Daten speichern wir zus&auml;tzlich zu den zu archivierenden Daten in der Archivtabelle und wie sieht diese dann aus Wir wollen die Kopien ge&auml;nderter Datens&auml;tze auf jeden Fall den neuen Versionen der Datens&auml;tze zuordnen k&ouml;nnen. Daher sollte die Tabelle mit den archivierten Datens&auml;tzen ein Feld zum Aufnehmen des Prim&auml;rschl&uuml;sselwertes des Originaldatensatzes enthalten. Au&szlig;erdem wollen wir noch speichern, wann der Datensatz ge&auml;ndert oder gel&ouml;scht wurde und nat&uuml;rlich, ob er nun ge&auml;ndert oder gel&ouml;scht wurde. Da es mehrere Versionen des gleichen Datensatzes in der Archivtabelle geben kann, k&ouml;nnen wir also nicht das Prim&auml;rschl&uuml;sselwert des Originaldatensatzes als Prim&auml;rschl&uuml;sselwert des archivierten Datensatzes verwenden, sondern m&uuml;ssen f&uuml;r die Archivtabelle ein eigenes Prim&auml;rschl&uuml;sselfeld vorsehen.<\/p>\n<p>Wir ben&ouml;tigen also alle Felder der Originaltabelle, ein neues Prim&auml;rschl&uuml;sselfeld sowie jeweils ein Feld f&uuml;r die Angabe des Datums und eines f&uuml;r die Aktion, welche f&uuml;r das Archivieren des Datensatzes verantwortlich ist &#8211; also L&ouml;schen oder Bearbeiten.<\/p>\n<p>Das Prim&auml;rschl&uuml;sselfeld wollen wir immer <b>ArchivID <\/b>nennen und die beiden anderen Felder sollen <b>Archivdatum <\/b>und <b>Archivart <\/b>hei&szlig;en.<\/p>\n<p>Aber ist das die beste L&ouml;sung Wir k&ouml;nnen alternativ auch zwei Felder hinzuf&uuml;gen, die <b>Loeschdatum <\/b>und <b>Aenderungsdatum <\/b>hei&szlig;en und je nach Vorgang das eine oder andere Feld f&uuml;llen. Wir entscheiden uns f&uuml;r die zuletzt vorgeschlagene Variante und wollen diese beiden Felder zu den Archivtabellen hinzuf&uuml;gen. Die Archivtabellen m&uuml;ssen wir nat&uuml;rlich auch noch entsprechend benennen. In diesem Fall wollen wir einfach den Namen der zu archivierenden Tabelle nutzen und dieser das Suffix <b>_Archiv <\/b>anh&auml;ngen. F&uuml;r die Tabelle <b>tblKunden <\/b>entsteht so beispielsweise die Tabelle <b>tblKunden_Archiv<\/b>.<\/p>\n<p><b>Archivtabelle im SQL Server Management Studio anlegen<\/b><\/p>\n<p>In Visual Studio legen wir die gew&uuml;nschte Tabelle am schnellsten wie folgt an:<\/p>\n<ul>\n<li>&Ouml;ffnen Sie SQL Server Management Studio.<\/li>\n<li>Navigieren Sie zu der gew&uuml;nschten Tabelle in der entsprechenden Datenbank.<\/li>\n<li>W&auml;hlen Sie f&uuml;r die Tabelle den Kontextmen&uuml;-Eintrag <b>Skript f&uuml;r Tabelle als|CREATE in|Neues Abfrage-Editor-Fenster <\/b>aus (siehe Bild 1).<\/li>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_06\/pic_1211_001.png\" alt=\"Archivtabelle anlegen\" width=\"700\" height=\"387,7041\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Archivtabelle anlegen<\/span><\/b><\/p>\n<\/ul>\n<p>Daraufhin erscheint ein neues Abfragefenster, das den SQL-Code zum Erstellen der Tabelle anzeigt, hier <b>tblKunden<\/b>. Hier nehmen Sie einige &Auml;nderungen vor. Die erste ist, dass wir den Tabellennamen in <b>tblKunden_Archiv <\/b>&auml;ndern. Danach f&uuml;gen wir das Feld <b>ArchivID <\/b>als neuen Prim&auml;rschl&uuml;sselwert hinzu und gestalten das Feld <b>KundeID <\/b>als normales <b>Integer<\/b>-Feld. Au&szlig;erdem legen wir am Ende noch die beiden Felder <b>Loeschdatum <\/b>und <b>Aenderungsdatum <\/b>an, jeweils mit dem Datentyp <b>DATETIME<\/b>. Au&szlig;erdem &auml;ndern wir noch den Namen des Prim&auml;rschl&uuml;sselfeldes f&uuml;r den <b>CONSTRAINT<\/b>-Abschnitt. Der SQL-Code sieht dann in gek&uuml;rzter Form wie folgt aus:<\/p>\n<pre>CREATE TABLE [dbo].[tblKunden_Archiv](\r\n&nbsp;&nbsp;&nbsp;&nbsp;[ArchivID] [int] IDENTITY(1,1) NOT NULL,\r\n&nbsp;&nbsp;&nbsp;&nbsp;[KundeID] [int] NULL,\r\n&nbsp;&nbsp;&nbsp;&nbsp;[KundenCode] [nvarchar](5) NULL,\r\n&nbsp;&nbsp;&nbsp;&nbsp;[Firma] [nvarchar](40) NOT NULL,\r\n&nbsp;&nbsp;&nbsp;&nbsp;[AnredeID] [int] NULL,\r\n&nbsp;&nbsp;&nbsp;&nbsp;[Vorname] [nvarchar](255) NULL,\r\n&nbsp;&nbsp;&nbsp;&nbsp;[Nachname] [nvarchar](255) NULL,\r\n&nbsp;&nbsp;&nbsp;&nbsp;[Position] [nvarchar](30) NULL,\r\n&nbsp;&nbsp;&nbsp;&nbsp;[PLZ] [nvarchar](10) NULL,\r\n&nbsp;&nbsp;&nbsp;&nbsp;[Strasse] [nvarchar](255) NULL,\r\n&nbsp;&nbsp;&nbsp;&nbsp;[Ort] [nvarchar](15) NULL,\r\n&nbsp;&nbsp;&nbsp;&nbsp;[Region] [nvarchar](15) NULL,\r\n&nbsp;&nbsp;&nbsp;&nbsp;[Land] [nvarchar](15) NULL,\r\n&nbsp;&nbsp;&nbsp;&nbsp;[Telefon] [nvarchar](24) NULL,\r\n&nbsp;&nbsp;&nbsp;&nbsp;[Telefax] [nvarchar](24) NULL,\r\n&nbsp;&nbsp;&nbsp;&nbsp;[Loeschdatum] [datetime] NULL,\r\n&nbsp;&nbsp;&nbsp;&nbsp;[Aenderungsdatum] [datetime] NULL\r\n  CONSTRAINT [tblKunden_Archiv$PrimaryKey] PRIMARY KEY CLUSTERED \r\n(\r\n&nbsp;&nbsp;&nbsp;&nbsp;[ArchivID] ASC\r\n)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]\r\n) ON [PRIMARY]<\/pre>\n<p><!--30percent--><\/p>\n<p>Nur diesen Teil wollen wir dann auch ausf&uuml;hren. L&ouml;schen Sie also den Rest des automatisch erzeugten Skriptes oder markieren Sie nur diesen Teil und f&uuml;hren Sie diesen mit <b>F5 <\/b>aus.<\/p>\n<p>Nach einer Aktualisierung des Knotens <b>Tabellen <\/b>im SQL Server Management Studio erscheint die Tabelle <b>tblKunden_Archiv <\/b>dann in der Liste der Tabellen und der Entwurf sieht wie in Bild 2 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_06\/pic_1211_002.png\" alt=\"Entwurf der neuen Archivtabelle\" width=\"549,6265\" height=\"521,6065\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Entwurf der neuen Archivtabelle<\/span><\/b><\/p>\n<p><b>Trigger im SQL Server Management Studio anlegen<\/b><\/p>\n<p>Nun wollen wir die notwendigen Trigger f&uuml;r die Tabelle <b>tblKunden <\/b>anlegen, die daf&uuml;r sorgen, dass ein zu l&ouml;schender oder zu &auml;ndernder Datensatz vor dem jeweiligen Vorgang in die Tabelle <b>tblKunden_Archiv <\/b>gesichert wird. Wenn man das Kontextmen&uuml; der Tabelle <b>tblKunden <\/b>durchsieht, findet man dort keine M&ouml;glichkeit, einen Trigger anzulegen. Allerdings gibt es unterhalb des Elements <b>tblKunden <\/b>ein weiteres Element namens <b>Trigger<\/b>, dessen Kontextmen&uuml; den Eintrag <b>Neuer Trigger&#8230; <\/b>liefert (siehe Bild 3).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_06\/pic_1211_003.png\" alt=\"Anlegen eines neuen Triggers\" width=\"424,7115\" height=\"532,2335\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Anlegen eines neuen Triggers<\/span><\/b><\/p>\n<p><b>Trigger beim Aktualisieren<\/b><\/p>\n<p>Dies &ouml;ffnet ein neues Abfragefenster mit einer Vorlage f&uuml;r einen neuen Trigger. Den hier abgebildeten Code ersetzen wir durch die von uns gew&uuml;nschte Version. Dabei wollen wir in einer <b>INSERT INTO<\/b>-Abfrage die Inhalte aller Felder des aktualisierten Datensatzes der Tabelle <b>tblKunden <\/b>in die entsprechenden Felder der Tabelle <b>tblKunden_Archiv <\/b>schreiben. Au&szlig;erdem soll das Feld <b>Aenderungsdatum <\/b>mit dem aktuellen Datum plus Uhrzeit gef&uuml;llt werden. Dazu &auml;ndern wir die <b>CREATE TRIGGER<\/b>-Anweisung wie folgt ab:<\/p>\n<pre>CREATE TRIGGER dbo.tblKunden_Update \r\n     ON  dbo.tblKunden \r\n     AFTER UPDATE\r\nAS \r\nBEGIN\r\nSET NOCOUNT ON;\r\nINSERT INTO dbo.tblKunden_Archiv(\r\n     [KundeID], [KundenCode], [Firma], [AnredeID], \r\n     [Vorname], [Nachname], [Position], [PLZ], [Strasse], \r\n     [Ort], [Region], [Land], [Telefon], [Telefax], \r\n     [Aenderungsdatum]\r\n)\r\nSELECT [KundeID], [KundenCode], [Firma], [AnredeID], \r\n     [Vorname], [Nachname], [Position], [PLZ], [Strasse], \r\n     [Ort], [Region], [Land], [Telefon], [Telefax], \r\n     GetDate() FROM deleted;\r\nEND<\/pre>\n<p>F&uuml;hren Sie diese Anweisung dann mit <b>F5 <\/b>aus, erscheint eine Erfolgsmeldung im Bereich <b>Meldungen<\/b>. Au&szlig;erdem finden Sie nach der Aktualisierung des Elements <b>Trigger <\/b>einen neuen Eintrag unterhalb dieses Elements vor &#8211; siehe Bild 4. Bevor wir diesen ausprobieren, eine kurze Erl&auml;uterung des Triggers. Wir haben diesen mit dem Schl&uuml;sselwort <b>AFTER UPDATE <\/b>ausgestattet, damit dieser nach Aktualisierungen an einem oder mehreren Datens&auml;tzen ausgel&ouml;st wird. Um zu diesem Zeitpunkt noch auf die Werte der Felder vor der &Auml;nderung des Datensatzes zugreifen zu k&ouml;nnen, verwenden wir im <b>SELECT<\/b>-Abschnitt den Alias <b>deleted <\/b>statt des Tabellennamens. Dem Feld <b>Aenderungsdatum <\/b>weisen wir das Ergebnis der eingebauten T-SQL-Funktion <b>GetDate <\/b>zu.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_06\/pic_1211_004.png\" alt=\"Der neue Trigger im Objekt-Explorer\" width=\"424,7115\" height=\"489,6139\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Der neue Trigger im Objekt-Explorer<\/span><\/b><\/p>\n<p>Um den Trigger zu testen, w&auml;hlen Sie aus dem Kontextmen&uuml; der Tabelle <b>tblKunden <\/b>den Eintrag <b>Erste 200 Datens&auml;tze bearbeiten <\/b>aus. Dann &auml;ndern wir beispielsweise den Wert des Feldes <b>Firma <\/b>von <b>Alfreds Futterkiste <\/b>auf <b>Andr&eacute;s Futterkiste<\/b>.<\/p>\n<p>&Ouml;ffnen wir danach die Tabelle <b>tblKunden_Archiv<\/b> auf die gleiche Weise, erhalten wir den neuen Datensatz aus Bild 5. Hier ist im Feld <b>Firma <\/b>noch der alte Wert enthalten. Au&szlig;erdem wurde im Feld <b>Aenderungsdatum <\/b>das aktuelle Datum plus Uhrzeit eingetragen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_06\/pic_1211_005.png\" alt=\"Ein automatisch archivierter Datensatz\" width=\"649,559\" height=\"216,8259\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Ein automatisch archivierter Datensatz<\/span><\/b><\/p>\n<p><b>Trigger beim L&ouml;schen<\/b><\/p>\n<p>Nun brauchen wir noch einen passenden Trigger f&uuml;r das L&ouml;schen von Datens&auml;tzen. Diesen legen wir auf die gleiche Art an. Das SQL-Skript, um diesen Trigger zu erstellen, lautet wie folgt:<\/p>\n<pre>CREATE TRIGGER [dbo].[tblKunden_Delete] \r\n     ON  [dbo].[tblKunden] \r\n     AFTER DELETE\r\nAS \r\nBEGIN\r\n     SET NOCOUNT ON;\r\n     INSERT INTO dbo.tblKunden_Archiv([KundeID], \r\n         [KundenCode], [Firma], [AnredeID], [Vorname], \r\n         [Nachname], [Position], [PLZ], [Strasse], [Ort], \r\n         [Region], [Land], [Telefon], [Telefax], \r\n         [Loeschdatum]) \r\n     SELECT [KundeID], [KundenCode], [Firma], [AnredeID], \r\n         [Vorname], [Nachname], [Position], [PLZ], \r\n         [Strasse], [Ort], [Region], [Land], [Telefon], \r\n         [Telefax], GetDate() FROM deleted;\r\nEND<\/pre>\n<p>Gegen&uuml;ber dem <b>UPDATE<\/b>-Trigger brauchen wir nur die folgenden Stellen zu &auml;ndern: den Namen des zu erstellenden Triggers (hier <b>tblKunden_Delete<\/b>) sowie die Aktion, bei welcher der Trigger ausgel&ouml;st werden soll (<b>AFTER DELETE<\/b>). Au&szlig;erdem soll der Wert der T-SQL-Funktion <b>GetDate <\/b>in das Feld <b>Loeschdatum <\/b>eingetragen werden und nicht in <b>Aenderungsdatum<\/b>.<\/p>\n<p>Nach dem Anlegen des Triggers mit der Taste <b>F5 <\/b>k&ouml;nnen wir diesen direkt testen. Wenn wir einen Datensatz l&ouml;schen und anschlie&szlig;end wieder die Tabelle <b>tblKunden_Archiv <\/b>&ouml;ffnen, finden wir dort einen neuen Datensatz vor, bei dem diesmal das Feld <b>Loeschdatum <\/b>mit einem Datum gef&uuml;llt ist (siehe Bild 6).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_06\/pic_1211_006.png\" alt=\"Archivierte Version eines gel&ouml;schten Datensatzes\" width=\"649,559\" height=\"192,1109\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Archivierte Version eines gel&ouml;schten Datensatzes<\/span><\/b><\/p>\n<p><b>DELETE und UPDATE in einem Trigger <\/b><\/p>\n<p>Nun wollen wir m&ouml;glichst wenige Objekte erzeugen. Dazu wollen wir pr&uuml;fen, ob wir den <b>DELETE<\/b>&#8211; und den <b>UPDATE<\/b>-Trigger auch in einem Trigger unterbringen k&ouml;nnen.<\/p>\n<p>Einen Trigger zu erzeugen, der auf beide Ereignisse reagiert, ist kein Problem &#8211; dazu m&uuml;ssen wir hinter das Schl&uuml;sselwort <b>AFTER <\/b>nur die beiden Schl&uuml;sselw&ouml;rter <b>DELETE <\/b>und <b>UPDATE <\/b>durch ein Komma voneinander getrennt angeben.<\/p>\n<p>Au&szlig;erdem m&uuml;ssen wir noch pr&uuml;fen, ob der Trigger durch einen <b>DELETE<\/b>&#8211; oder durch einen <b>UPDATE<\/b>-Vorgang ausgel&ouml;st wurde. Um das herauszufinden, verwenden wir eine <b>IF&#8230;ELSE<\/b>-Bedingung im Trigger. Damit pr&uuml;fen wir, ob die Tabelle <b>inserted <\/b>einen Datensatz enth&auml;lt oder nicht. Das ist nur der Fall, wenn der Benutzer einen Datensatz ge&auml;ndert hat &#8211; falls nicht, ist die Tabelle <b>inserted <\/b>leer. Nur die Tabelle <b>delete <\/b>enth&auml;lt in beiden F&auml;llen einen Datensatz.<\/p>\n<p>Die Anweisung zum Erstellen des Trigges k&ouml;nnen wir nun wie in Listing 1 definieren. Existiert der Datensatz, den die Abfrage <b>SELECT 1 FROM inserted <\/b>liefert, f&uuml;hren wir die <b>INSERT INTO<\/b>-Abfrage f&uuml;r das &Auml;ndern eines Datensatzes durch. Liefert die Abfrage keinen Datensatz, ist die <b>EXISTS<\/b>-Funktion falsch und der <b>ELSE<\/b>-Teil wird ausgef&uuml;hrt. In diesem Fall f&uuml;hren wir die <b>INSERT INTO<\/b>-Abfrage f&uuml;r das L&ouml;schen eines Datensatzes aus.<\/p>\n<pre>CREATE TRIGGER [dbo].[tblKunden_DeleteUpdate] \r\n     ON  [dbo].[tblKunden] \r\n     AFTER DELETE, UPDATE\r\nAS \r\nBEGIN\r\n&nbsp;&nbsp;&nbsp;&nbsp;SET NOCOUNT ON;\r\n&nbsp;&nbsp;&nbsp;&nbsp;IF EXISTS(SELECT 1 FROM inserted)\r\n&nbsp;&nbsp;&nbsp;&nbsp;    INSERT INTO dbo.tblKunden_Archiv([KundeID], [KundenCode], [Firma], [AnredeID], [Vorname], [Nachname], \r\n                 [Position], [PLZ], [Strasse], [Ort], [Region], [Land], [Telefon], [Telefax], [Aenderungsdatum]) \r\n             SELECT [KundeID], [KundenCode], [Firma], [AnredeID], [Vorname], [Nachname], [Position], [PLZ], [Strasse], \r\n                 [Ort], [Region], [Land], [Telefon], [Telefax], GetDate() FROM deleted;\r\n&nbsp;&nbsp;&nbsp;&nbsp;ELSE\r\n&nbsp;&nbsp;&nbsp;&nbsp;    INSERT INTO dbo.tblKunden_Archiv([KundeID], [KundenCode], [Firma], [AnredeID], [Vorname], [Nachname], \r\n                [Position], [PLZ], [Strasse], [Ort], [Region], [Land], [Telefon], [Telefax], [Loeschdatum]) \r\n             SELECT [KundeID], [KundenCode], [Firma], [AnredeID], [Vorname], [Nachname], [Position], [PLZ], [Strasse], \r\n                 [Ort], [Region], [Land], [Telefon], [Telefax], GetDate() FROM deleted;\r\nEND<\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Trigger zum Archivieren von Daten nach DELETE- oder UPDATE-Vorg&auml;ngen<\/span><\/b><\/p>\n<p>Der Unterschied besteht lediglich in dem Feld, das wir mit dem Wert der Funktion <b>GetDate <\/b>f&uuml;llen. Daf&uuml;r eine solch lange Abfrage zwei Mal zu schreiben, ist fehleranf&auml;llig &#8211; vor allem, wenn sich der Entwurf der Tabelle &auml;ndert. Dann m&uuml;ssen wir die &Auml;nderungen im Trigger gleich an mehreren Stellen durchf&uuml;hren. Wir k&ouml;nnen auch erst die <b>INSERT INTO<\/b>-Anweisung ausf&uuml;hren, ohne die Felder <b>Loeschdatum <\/b>oder <b>Aenderungsdatum <\/b>zu f&uuml;llen und das dann nachholen:<\/p>\n<pre>INSERT INTO dbo.tblKunden_Archiv([KundeID], [KundenCode], ...) SELECT [KundeID], [KundenCode], ... FROM deleted;\r\nIF EXISTS(SELECT 1 FROM inserted)\r\n     UPDATE dbo.tblKunden_Archiv         SET Aenderungsdatum = GETDATE() \r\n         WHERE tblKunden_Archiv.ArchivID = @@IDENTITY\r\nELSE\r\n     UPDATE dbo.tblKunden_Archiv \r\n         SET Loeschdatum = GETDATE() \r\n         WHERE tblKunden_Archiv.ArchivID = @@IDENTITY<\/pre>\n<p><b>Zusammenfassung und Ausblick<\/b><\/p>\n<p>Wenn Sie wie in diesem Beitrag beschrieben eine Archivtabelle anlegen und der Originaltabelle zwei Trigger hinzuf&uuml;gen, um die Originaldatens&auml;tze nach dem L&ouml;schen oder &Auml;ndern in der Archivtabelle zu speichern, verlieren Sie nach dem &Auml;ndern oder L&ouml;schen keine Daten mehr. Diese k&ouml;nnen Sie dann auch auf Basis der gesicherten Daten wiederherstellen.<\/p>\n<p>In einem weiteren Beitrag namens <b>Datenhistorie-Trigger schnell anlegen <\/b>(<b>www.access-im-unternehmen.de\/****<\/b>) erfahren Sie, wie Sie die hier vorgestellten Archivtabellen und Trigger per Mausklick anlegen k&ouml;nnen.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wer seine Access-Tabellen von einem Access-Backend in ein SQL Server-Backend &uuml;bertragen hat, d&uuml;rfte zun&auml;chst keinen Unterschied beim Zugriff auf die Daten bemerken. Spannend wird es, wenn Sie unter Access jedoch die sogenannten Datenmakros verwendet haben, um automatisch auf &Auml;nderungen in den Daten zu reagieren und beispielsweise Kopien ge&auml;nderter oder gel&ouml;schter Datens&auml;tze in einer Archivtabelle angelegt haben. Beim Migrieren nach SQL Server werden zwar auch die Archivtabellen erstellt, aber die Datenmakros bleiben au&szlig;en vor. Damit keine Daten verloren gehen, zeigen wir in diesem Beitrag, wie Sie die Tabellen mit Triggern ausstatten, um die gew&uuml;nschten Daten zu archivieren.<\/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":[662019,66062019,44000022],"tags":[],"class_list":["post-55001211","post","type-post","status-publish","format-standard","hentry","category-662019","category-66062019","category-SQL_Server_und_Co"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.9 (Yoast SEO v27.3) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Datenhistorie per Trigger - 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\/Datenhistorie_per_Trigger\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Datenhistorie per Trigger\" \/>\n<meta property=\"og:description\" content=\"Wer seine Access-Tabellen von einem Access-Backend in ein SQL Server-Backend &uuml;bertragen hat, d&uuml;rfte zun&auml;chst keinen Unterschied beim Zugriff auf die Daten bemerken. Spannend wird es, wenn Sie unter Access jedoch die sogenannten Datenmakros verwendet haben, um automatisch auf &Auml;nderungen in den Daten zu reagieren und beispielsweise Kopien ge&auml;nderter oder gel&ouml;schter Datens&auml;tze in einer Archivtabelle angelegt haben. Beim Migrieren nach SQL Server werden zwar auch die Archivtabellen erstellt, aber die Datenmakros bleiben au&szlig;en vor. Damit keine Daten verloren gehen, zeigen wir in diesem Beitrag, wie Sie die Tabellen mit Triggern ausstatten, um die gew&uuml;nschten Daten zu archivieren.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Datenhistorie_per_Trigger\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2020-07-10T09:43:56+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg05.met.vgwort.de\/na\/7fa82b1cfa8d44f6a84ff62172c03ba0\" \/>\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=\"12\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenhistorie_per_Trigger\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenhistorie_per_Trigger\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Datenhistorie per Trigger\",\"datePublished\":\"2020-07-10T09:43:56+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenhistorie_per_Trigger\\\/\"},\"wordCount\":1878,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenhistorie_per_Trigger\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg05.met.vgwort.de\\\/na\\\/7fa82b1cfa8d44f6a84ff62172c03ba0\",\"articleSection\":[\"2019\",\"6\\\/2019\",\"SQL Server und Co.\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenhistorie_per_Trigger\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenhistorie_per_Trigger\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenhistorie_per_Trigger\\\/\",\"name\":\"Datenhistorie per Trigger - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenhistorie_per_Trigger\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenhistorie_per_Trigger\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg05.met.vgwort.de\\\/na\\\/7fa82b1cfa8d44f6a84ff62172c03ba0\",\"datePublished\":\"2020-07-10T09:43:56+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenhistorie_per_Trigger\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenhistorie_per_Trigger\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenhistorie_per_Trigger\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg05.met.vgwort.de\\\/na\\\/7fa82b1cfa8d44f6a84ff62172c03ba0\",\"contentUrl\":\"http:\\\/\\\/vg05.met.vgwort.de\\\/na\\\/7fa82b1cfa8d44f6a84ff62172c03ba0\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenhistorie_per_Trigger\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Datenhistorie per Trigger\"}]},{\"@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":"Datenhistorie per Trigger - 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\/Datenhistorie_per_Trigger\/","og_locale":"de_DE","og_type":"article","og_title":"Datenhistorie per Trigger","og_description":"Wer seine Access-Tabellen von einem Access-Backend in ein SQL Server-Backend &uuml;bertragen hat, d&uuml;rfte zun&auml;chst keinen Unterschied beim Zugriff auf die Daten bemerken. Spannend wird es, wenn Sie unter Access jedoch die sogenannten Datenmakros verwendet haben, um automatisch auf &Auml;nderungen in den Daten zu reagieren und beispielsweise Kopien ge&auml;nderter oder gel&ouml;schter Datens&auml;tze in einer Archivtabelle angelegt haben. Beim Migrieren nach SQL Server werden zwar auch die Archivtabellen erstellt, aber die Datenmakros bleiben au&szlig;en vor. Damit keine Daten verloren gehen, zeigen wir in diesem Beitrag, wie Sie die Tabellen mit Triggern ausstatten, um die gew&uuml;nschten Daten zu archivieren.","og_url":"https:\/\/access-im-unternehmen.de\/Datenhistorie_per_Trigger\/","og_site_name":"Access im Unternehmen","article_published_time":"2020-07-10T09:43:56+00:00","og_image":[{"url":"http:\/\/vg05.met.vgwort.de\/na\/7fa82b1cfa8d44f6a84ff62172c03ba0","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"12\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/Datenhistorie_per_Trigger\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Datenhistorie_per_Trigger\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Datenhistorie per Trigger","datePublished":"2020-07-10T09:43:56+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Datenhistorie_per_Trigger\/"},"wordCount":1878,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Datenhistorie_per_Trigger\/#primaryimage"},"thumbnailUrl":"http:\/\/vg05.met.vgwort.de\/na\/7fa82b1cfa8d44f6a84ff62172c03ba0","articleSection":["2019","6\/2019","SQL Server und Co."],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Datenhistorie_per_Trigger\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Datenhistorie_per_Trigger\/","url":"https:\/\/access-im-unternehmen.de\/Datenhistorie_per_Trigger\/","name":"Datenhistorie per Trigger - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Datenhistorie_per_Trigger\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Datenhistorie_per_Trigger\/#primaryimage"},"thumbnailUrl":"http:\/\/vg05.met.vgwort.de\/na\/7fa82b1cfa8d44f6a84ff62172c03ba0","datePublished":"2020-07-10T09:43:56+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Datenhistorie_per_Trigger\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Datenhistorie_per_Trigger\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/Datenhistorie_per_Trigger\/#primaryimage","url":"http:\/\/vg05.met.vgwort.de\/na\/7fa82b1cfa8d44f6a84ff62172c03ba0","contentUrl":"http:\/\/vg05.met.vgwort.de\/na\/7fa82b1cfa8d44f6a84ff62172c03ba0"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Datenhistorie_per_Trigger\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Datenhistorie per Trigger"}]},{"@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\/55001211","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=55001211"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001211\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001211"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001211"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001211"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}