{"id":55001212,"date":"2019-12-01T00:00:00","date_gmt":"2020-07-10T09:44:10","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1212"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"DatenhistorieTrigger_schnell_anlegen","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/DatenhistorieTrigger_schnell_anlegen\/","title":{"rendered":"Datenhistorie-Trigger schnell anlegen"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg05.met.vgwort.de\/na\/d7f310363c674b64906407880e2adade\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>Im Beitrag &#8222;Datenhistorie per Trigger&#8220; haben wir gezeigt, welche Anweisungen n&ouml;tig sind, um eine Archivtabelle anzulegen und der Originaltabelle einen Trigger hinzuzuf&uuml;gen, der beim &Auml;ndern oder L&ouml;schen eines Datensatzes die vorherige Version in einer Archivtabelle speichert. Im vorliegenden Beitrag wollen wir eine VBA-Prozedur entwickeln, mit der Sie zu einer Tabelle mit einem einfachen Aufruf die Archivtabelle und den Trigger in einem Rutsch anlegen k&ouml;nnen. Damit sichern Sie Ihre Daten bei &Auml;nderungen auch f&uuml;r mehrere Tabellen ganz schnell ab.<\/b><\/p>\n<p><b>Voraussetzungen<\/b><\/p>\n<p>In diesem Beitrag kommen die bereits im Beitrag <b>Datenhistorie per Trigger <\/b>(siehe <b>www.access-im-unternehmen.de\/1211<\/b>) erw&auml;hnten Access-SQL-Tools zum Einsatz. Diese haben wir wiederum bereits im Beitrag <b>SQL Server-Tools <\/b>vorgestellt (siehe <b>www.access-im-unternehmen.de\/1061<\/b>).<\/p>\n<p><b>Ziel des Beitrags<\/b><\/p>\n<p>Das Ziel ist es nun, eine Prozedur zu entwickeln, der Sie nur den Namen der Tabelle &uuml;bergeben, f&uuml;r welche eine Archivtabelle erstellt werden soll und die einen Trigger erhalten soll, der beim &Auml;ndern oder L&ouml;schen von Datens&auml;tzen aus dieser Tabelle die vorherige Version des Datensatzes in der Archivtabelle speichert.<\/p>\n<p>Dazu haben wir zun&auml;chst alle Elemente der SQL Server-Tools in die Beispieldatenbank integriert.<\/p>\n<p><b>Prozedur zum Erstellen der Archivtabelle<\/b><\/p>\n<p>Im oben genannten Beitrag <b>Datenhistorie per Trigger <\/b>haben wir eine Archivtabelle mit einer SQL-Anweisung wie der folgenden erstellt:<\/p>\n<pre>CREATE TABLE [dbo].[tblKunden_Archiv](\r\n     [ArchivID] [int] IDENTITY(1,1) NOT NULL,\r\n     [KundeID] [int] NULL,\r\n     ...\r\n     [Loeschdatum] [datetime] NULL,\r\n     [Aenderungsdatum] [datetime] NULL\r\nCONSTRAINT [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>Diese Anweisung wollen wir nun parametrisieren, damit wir diese f&uuml;r beliebige Tabellennamen erstellen k&ouml;nnen. Dazu ben&ouml;tigen wir etwas Wissen &uuml;ber die Tabelle, f&uuml;r die wir die Archivtabelle anlegen wollen. Da wir nicht davon ausgehen k&ouml;nnen, dass es eine per ODBC erstellte Verkn&uuml;pfung auf diese Tabelle gibt, wollen wir die Felder und ihre Eigenschaften direkt vom SQL Server beziehen. Um die Feldnamen und die Datentypen zu ermitteln, k&ouml;nnen wir die folgende Abfrage nutzen, die wir direkt an den SQL Server absetzen:<\/p>\n<pre>SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'tblKunden'<\/pre>\n<p>Im SQL Server sieht das Ergebnis f&uuml;r die Tabelle <b>tblKunden <\/b>wie in Bild 1 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_06\/pic_1212_001.png\" alt=\"Ausgabe der Abfrage der Eigenschaften der Felder einer Tabelle\" width=\"700\" height=\"235,5641\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Ausgabe der Abfrage der Eigenschaften der Felder einer Tabelle<\/span><\/b><\/p>\n<p><b>Funktion zum Einlesen der Feldliste f&uuml;r CREATE TABLE<\/b><\/p>\n<p>Wir ben&ouml;tigen eine Funktion, mit der wir die in der Archivtabelle anzulegenden Felder ermitteln.<\/p>\n<p>Diese hei&szlig;t <b>FeldlisteZusammenstellen <\/b>und sieht wie in Listing 1 aus.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>FeldlisteZusammenstellen(strTabelle<span style=\"color:blue;\"> As String<\/span>, strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>strFeldliste<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> rst = mdlToolsSQLServer.SQLRecordset(\"SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '\" _\r\n         & strTabelle & \"'\", strVerbindungszeichenfolge)\r\n     strFeldliste = \"    [ArchivID] [int] IDENTITY(1,1) NOT NULL,\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rst.EOF\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> rst!DATA_TYPE = \"Timestamp\"<span style=\"color:blue;\"> Then<\/span>\r\n             strFeldliste = strFeldliste & \"    [\" & rst!COLUMN_NAME & \"]\"\r\n             strFeldliste = strFeldliste & \" [\" & rst!DATA_TYPE & \"]\"\r\n             <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> IsNull(rst!Character_maximum_Length)<span style=\"color:blue;\"> Then<\/span>\r\n                 <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> rst!Character_maximum_Length = -1<span style=\"color:blue;\"> Then<\/span>\r\n                     strFeldliste = strFeldliste & \"(\" & rst!Character_maximum_Length & \")\"\r\n                 <span style=\"color:blue;\">Else<\/span>\r\n                     strFeldliste = strFeldliste & \"(max)\"\r\n                 <span style=\"color:blue;\">End If<\/span>\r\n             <span style=\"color:blue;\">End If<\/span>\r\n             <span style=\"color:blue;\">If <\/span>rst!IS_NULLABLE = \"YES\"<span style=\"color:blue;\"> Then<\/span>\r\n                 strFeldliste = strFeldliste & \" NULL\"\r\n             <span style=\"color:blue;\">Else<\/span>\r\n                 strFeldliste = strFeldliste & \" NOT NULL\"\r\n             <span style=\"color:blue;\">End If<\/span>\r\n             strFeldliste = strFeldliste & \",\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n         rst.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     strFeldliste = strFeldliste & \"    [Loeschdatum] [datetime] NULL,\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strFeldliste = strFeldliste & \"    [Aenderungsdatum] [datetime] NULL\"\r\n     FeldlisteZusammenstellen = strFeldliste\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Funktion zum Ermitteln der Feldliste<\/span><\/b><\/p>\n<p>Die Funktion nimmt den Namen der zu untersuchenden Tabelle sowie die Verbindungszeichenfolge der SQL Server-Datenbank als Parameter entgegen und liefert einen String als Ergebnis zur&uuml;ck. Die Funktion verwendet die Funktion <b>SQLRecordset <\/b>des Moduls <b>mdlToolsSQLServer<\/b>, um die Datens&auml;tze der Tabelle <b>INFORMATION_SCHEMA.COLUMNS <\/b>zur&uuml;ckzuliefern.<\/p>\n<p>Dabei wollen wir nach dem Feld <b>TABLE_NAME <\/b>filtern, das die Datens&auml;tze auf diejenigen einschr&auml;nken soll, deren Tabellenname dem Wert des Parameters <b>strTabelle <\/b>entspricht. Auch diese Funktion nimmt wieder die Verbindungszeichenfolge als Parameter entgegen.<\/p>\n<p>Danach stellt die Funktion <b>FeldlisteZusammenstellen <\/b>in der Variablen <b>strFeldliste <\/b>die Liste der anzulegenden Felder f&uuml;r die Archivtabelle zusammen. Bevor wir die Datens&auml;tze des Recordsets <b>rst <\/b>durchlaufen, f&uuml;gen wir dort bereits als ersten Eintrag den folgenden hinzu:<\/p>\n<pre>[ArchivID] [int] IDENTITY(1,1) NOT NULL,<\/pre>\n<p>Dies entspricht dem Prim&auml;rschl&uuml;sselfeld f&uuml;r die Archivtabelle. Das Komma am Ende deutet bereits an, dass nun weitere Felder folgen. Diese stellen wir in einer <b>Do While<\/b>-Schleife &uuml;ber alle Datens&auml;tze der Tabelle zusammen. Ein Feld wird nur hinzugef&uuml;gt, wenn es nicht den Datentyp <b>Timestamp <\/b>hat, was wir in einer <b>If&#8230;Then<\/b>-Bedingung pr&uuml;fen.<\/p>\n<p>Ist das nicht der Fall, ber&uuml;cksichtigen wir als Erstes den Inhalt des Feldes <b>COLUMN_NAME <\/b>und damit den Feldnamen des anzulegenden Feldes, den wir in eckige Klammern einfassen.<\/p>\n<p>Dann folgt der Datentyp, den wir aus dem Feld <b>DATA_TYPE <\/b>des Recordsets entnehmen und den wir durch ein Leerzeichen vom Feldnamen getrennt ebenfalls in eckige Klammern einfassen. Dann pr&uuml;fen wir, ob das Feld <b>CHAR-ACTER_MAXIMUM_LENGTH <\/b>einen Wert enth&auml;lt. Dieses liefert die Anzahl der Zeichen f&uuml;r Textfelder. Liegt ein Wert vor, f&uuml;gen wir diesen in runde Klammern eingefasst zum Ausdruck in <b>strFeldListe <\/b>ein. Aber es kann auch sein, dass der Wert <b>-1 <\/b>betr&auml;gt. Dieser tritt auf, wenn der eigentliche Wert <b>max <\/b>lautet, was auf ein langes Textfeld hinweist. In diesem Fall wollen wir nicht <b>-1<\/b>, sondern <b>max <\/b>in runden Klammern zur Felddefinition hinzuf&uuml;gen.<\/p>\n<p>Schlie&szlig;lich folgt das Feld <b>ISNULLABLE<\/b>, das mit den Werten <b>YES <\/b>und <b>NO <\/b>angibt, ob das Feld Nullwerte aufnehmen darf oder nicht. Im Falle von <b>YES <\/b>f&uuml;gen wir <b>NULL <\/b>zur Felddefinition hinzu, anderenfalls <b>NOT NULL<\/b>. Anschlie&szlig;end h&auml;ngen wir noch ein abschlie&szlig;endes Komma an die Beschreibung dieses Feldes an und gehen dann in die n&auml;chste Runde der <b>Do While<\/b>-Schleife.<\/p>\n<p>Sind alle Felder durchlaufen, fehlen noch die beiden Felder <b>Loeschdatum <\/b>und <b>Aenderungsdatum<\/b>, die beide mit dem Datentyp <b>datetime <\/b>angelegt werden sollen und Nullwerte enthalten d&uuml;rfen.<\/p>\n<p>Jeder Felddefinition f&uuml;gen wir au&szlig;erdem noch einen Zeilenumbruch hinzu, sodass das Ergebnis der Funktion beispielsweise f&uuml;r die Tabelle <b>tblKunden <\/b>wie folgt aussieht:<\/p>\n<pre>    [ArchivID] [int] IDENTITY(1,1) NOT NULL,\r\n     [KundeID] [int] NOT NULL,\r\n     [KundenCode] [nvarchar](5) NULL,\r\n     [Firma] [nvarchar](40) NOT NULL,\r\n     [AnredeID] [int] NULL,\r\n     [Vorname] [nvarchar](255) NULL,\r\n     [Nachname] [nvarchar](255) NULL,\r\n     [Position] [nvarchar](30) NULL,\r\n     [PLZ] [nvarchar](10) NULL,\r\n     [Strasse] [nvarchar](255) NULL,\r\n     [Ort] [nvarchar](15) NULL,\r\n     [Region] [nvarchar](15) NULL,\r\n     [Land] [nvarchar](15) NULL,\r\n     [Telefon] [nvarchar](24) NULL,\r\n     [Telefax] [nvarchar](24) NULL,\r\n     [Loeschdatum] [datetime] NULL,\r\n     [Aenderungsdatum] [datetime] NULL<\/pre>\n<p>Diese Zeichenkette wird dann als Ergebnis der Funktion zur&uuml;ckgegeben. Den Aufruf der Funktion zum Testen k&ouml;nnen Sie so gestalten:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>FieldlisteCreate_Test()\r\n     <span style=\"color:blue;\">Dim <\/span>strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>\r\n     strVerbindungszeichenfolge =  VerbindungszeichenfolgeNachID(10)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> FieldlistCreate(\"tblKunden\",  strVerbindungszeichenfolge)\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Hier nutzen wir die Funktion <b>VerbindungszeichenfolgeNachID<\/b>, um die Verbindungszeichenfolge zu ermitteln. Die SQL Server-Tools enthalten eine Tabelle namens <b>tblVerbindungszeichenfolgen<\/b>, welche die Informationen f&uuml;r die Verbindungszeichenfolgen speichert.<\/p>\n<p>Mit dem Formular <b>frmVerbindungszeichenfolgen <\/b>k&ouml;nnen Sie die Verbindungszeichenfolge zusammenstellen und auch testen. Merken Sie sich die ID der dabei gespeicherten Verbindungszeichenfolge und &uuml;bergeben Sie diese der Funktion <b>VerbindungszeichenfolgeNachID <\/b>als Parameter.<\/p>\n<p><b>Funktion CreateTableZusammenstellen<\/b><\/p>\n<p>Die Funktion aus Listing 2 stellt die aus der Funktion <b>FeldlisteZusammenstellen <\/b>ermittelten Felder mit einigen weiteren Zeilen zu der kompletten <b>CREATE TABLE<\/b>-Anweisung zusammen. Diese Funktion erwartet die gleichen Parameter wie die Funktion <b>FeldlisteZusammenstellen<\/b>.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>CreateTableZusammenstellen(strTabelle<span style=\"color:blue;\"> As String<\/span>, strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strCreateTable<span style=\"color:blue;\"> As String<\/span>\r\n     strCreateTable = \"CREATE TABLE [dbo].[\" & strTabelle & \"_Archiv](\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strCreateTable = strCreateTable & FeldlisteZusammenstellen(strTabelle, strVerbindungszeichenfolge) & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strCreateTable = strCreateTable & \"CONSTRAINT [\" & strTabelle & \"_Archiv$PrimaryKey] PRIMARY KEY CLUSTERED\" _\r\n         & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strCreateTable = strCreateTable & \"(\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strCreateTable = strCreateTable & \"    [ArchivID] Asc\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strCreateTable = strCreateTable & \")WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, \" _\r\n         & \"ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strCreateTable = strCreateTable & \") ON [PRIMARY]\"\r\n     CreateTableZusammenstellen = strCreateTable\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Funktion zum Ermitteln der Definition der Archivtabelle<\/span><\/b><\/p>\n<p><b>Archivtabelle erstellen<\/b><\/p>\n<p>Mit dem Ergebnis k&ouml;nnen wir nun bereits die Archivtabelle erstellen. W&auml;hrend wir die hier zusammengestellte <b>CREATE TABLE<\/b>-Anweisung normalerweise im SQL Server Management Studio ausf&uuml;hren w&uuml;rden, wollen wir dies auch von Access aus erledigen. Dazu liefern die SQL Server Tools auch eine Funktion. Diese hei&szlig;t <b>SQLAktionsabfrageOhneErgebnis <\/b>und erwartet die auszuf&uuml;hrende Abfrage sowie die Verbindungszeichenfolge als Parameter und einen optionalen Parameter f&uuml;r die R&uuml;ckgabe einer eventuellen Fehlermeldung.<\/p>\n<p>Diese Funktion rufen wir von einer anderen Funktion namens <b>ArchivtabelleErstellen<\/b> auf, die Sie in Listing 3 finden. Diese ermittelt zun&auml;chst die Verbindungszeichenfolge und mit der Funktion <b>CreateTableZusammenstellen <\/b>die SQL-Anweisung zum Erstellen der Archivtabelle. Dann ruft sie die Funktion <b>SQLAktionsabfrageOhneErgebnis <\/b>auf und speichert das Ergebnis in der Variablen <b>lngErgebnis<\/b>. Au&szlig;erdem verwendet sie als R&uuml;ckgabeparameter die Variable <b>strFehlermeldung<\/b>. Liefert <b>SQLAktionsabfrageOhneErgebnis <\/b>einen Wert ungleich <b>0<\/b>, k&ouml;nnen Sie der Variablen <b>strFehlermeldung <\/b>den Text der Meldung des aufgetretenen Fehlers entnehmen.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>ArchivtabelleErstellen()<span style=\"color:blue;\"> As Boolean<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strTabelle<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strCreateTable<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strFehlermeldung<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngErgebnis<span style=\"color:blue;\"> As Long<\/span>\r\n     strTabelle = \"tblKunden\"\r\n     strVerbindungszeichenfolge = VerbindungszeichenfolgeNachID(10)\r\n     strCreateTable = CREATETABLEZusammenstellen(strTabelle, strVerbindungszeichenfolge)\r\n     lngErgebnis = SQLAktionsabfrageOhneErgebnis(strCreateTable, strVerbindungszeichenfolge, strFehlermeldung)\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> lngErgebnis = 0<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Debug.Print<\/span> lngErgebnis, strFehlermeldung\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         ArchivtabelleErstellen = <span style=\"color:blue;\">True<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><!--30percent--><\/p>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Funktion zum Anlegen der Archivtabelle<\/span><\/b><\/p>\n<p>Ob das Ausf&uuml;hren der Abfrage erfolgreich war, pr&uuml;fen wir in einer <b>If&#8230;Then<\/b>-Bedingung, in der wir gegebenenfalls die Fehlermeldung im Direktfenster ausgeben. Au&szlig;erdem wird der R&uuml;ckgabewert der Funktion <b>ArchivtabelleErstellen <\/b>nur dann auf <b>True <\/b>eingestellt, wenn kein Fehler aufgetreten ist. Ein Fehler tritt beispielsweise auf, wenn wir eine Archivtabelle erstellen wollen, die bereits vorhanden ist. Die Meldung lautet dann:<\/p>\n<pre>[Microsoft][SQL Server Native Client 11.0][SQL Server]There is already an object named 'tblKunden_Archiv' in the database.<\/pre>\n<p><b>Prozedur zum Erstellen des Triggers<\/b><\/p>\n<p>Nun wollen wir die Funktion zum Erstellen des Triggers zusammenstellen. Der Trigger soll, am Beispiel der Tabelle <b>tblKunden<\/b>, gek&uuml;rzt wie folgt aussehen:<\/p>\n<pre>CREATE TRIGGER [dbo].[tblKunden_DeleteUpdate] \r\n     ON  [dbo].[tblKunden] \r\n     AFTER DELETE, UPDATE\r\nAS \r\nBEGIN\r\n     SET NOCOUNT ON;\r\n     INSERT INTO dbo.tblKunden_Archiv([KundeID], \r\n         [KundenCode], ...) SELECT [KundeID], \r\n         [KundenCode], ... FROM deleted;\r\n     IF EXISTS(SELECT 1 FROM inserted)\r\n         UPDATE dbo.tblKunden_Archiv \r\n             SET Aenderungsdatum = GETDATE() \r\n             WHERE tblKunden_Archiv.ArchivID = @@IDENTITY\r\n     ELSE\r\n         UPDATE dbo.tblKunden_Archiv \r\n             SET Loeschdatum = GETDATE() \r\n             WHERE tblKunden_Archiv.ArchivID = @@IDENTITY\r\nEND<\/pre>\n<p>Dazu ben&ouml;tigen wir zun&auml;chst einmal wieder eine Liste aller Felder der Tabelle. Diese lesen wir mithilfe der Funktion <b>FeldlisteTriggerZusammenstellen <\/b>aus Listing 4 ein. Die Funktion arbeitet grunds&auml;tzlich wie die bereits weiter oben vorgestellte Funktion <b>FeldlisteZusammenstellen<\/b>, mit der wir die Felddefinitionen f&uuml;r die <b>CREATE TABLE<\/b>-Anweisung zusammengestellt haben. In diesem Fall stellen wir allerdings lediglich eine kommaseparierte Liste aller Felder der per Parameter &uuml;bergebenen Tabelle zusammen. Die Feldnamen sollen dabei wieder in eckige Klammern eingefasst werden. Nach dem Durchlaufen aller Datens&auml;tze des Recordsets mit den Feldern der zu untersuchenden Tabelle, bei dem wir jedem Feld ein Komma vorangestellt haben, wird das Komma vor dem ersten Feld entfernt. Das Ergebnis dieser Funktion lautet am Beispiel der Tabelle <b>tblKunden <\/b>wie folgt:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>FeldlisteTriggerZusammenstellen(strTabelle<span style=\"color:blue;\"> As String<\/span>, strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>strFeldliste<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> rst = mdlToolsSQLServer.SQLRecordset(\"SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '\" _\r\n         & strTabelle & \"'\", strVerbindungszeichenfolge)\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rst.EOF\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> rst!DATA_TYPE = \"timestamp\"<span style=\"color:blue;\"> Then<\/span>\r\n             strFeldliste = strFeldliste & \", [\" & rst!COLUMN_NAME & \"]\"\r\n         <span style=\"color:blue;\">End If<\/span>\r\n         rst.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     strFeldliste = <span style=\"color:blue;\">Mid<\/span>(strFeldliste, 3)\r\n     FeldlisteTriggerZusammenstellen = strFeldliste\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Funktion zum Zusammenstellen der Feldliste f&uuml;r den Trigger<\/span><\/b><\/p>\n<pre>[KundeID], [KundenCode], [Firma], [AnredeID], [Vorname], [Nachname], [Position], [PLZ], [Strasse], [Ort], [Region], [Land], [Telefon], [Telefax]<\/pre>\n<p>Diese Funktion k&ouml;nnen Sie mit der folgenden Prozedur testen:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>FeldlisteTriggerZusammenstellen_Test()\r\n     <span style=\"color:blue;\">Dim <\/span>strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>\r\n     strVerbindungszeichenfolge =  VerbindungszeichenfolgeNachID(10)\r\n     <span style=\"color:blue;\">Debug.Print<\/span> FeldlisteTriggerZusammenstellen( \"tblKunden\", strVerbindungszeichenfolge)\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b>Funktion zum Zusammenstellen der CREATE TRIGGER-Anweisung<\/b><\/p>\n<p>Die Funktion <b>CREATETRIGGERZusammenstellen<\/b> aus Listing 5 stellt die <b>CREATE TRIGGER<\/b>-Anweisung zusammen. Dabei erwartet sie wie die anderen Funktionen den Namen der zu verwendenden Tabelle sowie die Verbindungszeichenfolge als Parameter.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>CREATETRIGGERZusammenstellen(strTabelle<span style=\"color:blue;\"> As String<\/span>, strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>strCreateTrigger<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strFeldliste<span style=\"color:blue;\"> As String<\/span>\r\n     strFeldliste = FeldlisteTriggerZusammenstellen(strTabelle, strVerbindungszeichenfolge)\r\n     strCreateTrigger = \"CREATE TRIGGER [dbo].[\" & strTabelle & \"_DeleteUpdate]\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strCreateTrigger = strCreateTrigger & \"    ON  [dbo].[\" & strTabelle & \"]\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strCreateTrigger = strCreateTrigger & \"    AFTER Delete, Update\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strCreateTrigger = strCreateTrigger & \"AS\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strCreateTrigger = strCreateTrigger & \"BEGIN\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strCreateTrigger = strCreateTrigger & \"    SET NOCOUNT ON;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strCreateTrigger = strCreateTrigger & \"    INSERT INTO dbo.\" & strTabelle & \"_Archiv(\" & strFeldliste _\r\n         & \") SELECT \" & strFeldliste & \" FROM deleted;\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strCreateTrigger = strCreateTrigger & \"    IF EXISTS(SELECT 1 FROM inserted)\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strCreateTrigger = strCreateTrigger & \"        UPDATE dbo.\" & strTabelle & \"_Archiv SET Aenderungsdatum = \" _\r\n         & \"GETDATE() WHERE \" & strTabelle & \"_Archiv.ArchivID = @@IDENTITY\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strCreateTrigger = strCreateTrigger & \"    Else\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strCreateTrigger = strCreateTrigger & \"        UPDATE dbo.\" & strTabelle & \"_Archiv SET Loeschdatum = \" _\r\n         & \"GETDATE() WHERE \" & strTabelle & \"_Archiv.ArchivID = @@IDENTITY\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strCreateTrigger = strCreateTrigger & \"End\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     CREATETRIGGERZusammenstellen = strCreateTrigger\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 5: Funktion zum Zusammenstellen der Abfrage zum Zusammenstellen der CREATE TRIGGER-Anweisung<\/span><\/b><\/p>\n<p>Dann ermittelt sie zun&auml;chst mit der Funktion <b>Feldliste-TriggerZusammenstellen <\/b>die Feldliste f&uuml;r den Trigger.<\/p>\n<p>Dann stellt sie in der Variablen <b>strCreateTrigger <\/b>die &uuml;brigen Zeilen des Triggers zusammen. Dabei f&uuml;gt sie an einigen Stellen den Namen der Tabelle aus <b>strTabelle <\/b>ein.<\/p>\n<p>Den Trigger erstellen wir dann mit der Funktion aus Listing 6. Diese arbeitet &auml;hnlich wie die Funktion zum Anlegen der Archivtabelle und liefert wieder eine Fehlermeldung, wenn das Anlegen des Triggers nicht funktioniert hat.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>TriggerErstellen()\r\n     <span style=\"color:blue;\">Dim <\/span>strTabelle<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strCreateTrigger<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strFehlermeldung<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngErgebnis<span style=\"color:blue;\"> As Long<\/span>\r\n     strTabelle = \"tblKunden\"\r\n     strVerbindungszeichenfolge = VerbindungszeichenfolgeNachID(10)\r\n     strCreateTrigger = CREATETRIGGERZusammenstellen(strTabelle, strVerbindungszeichenfolge)\r\n     lngErgebnis = SQLAktionsabfrageOhneErgebnis(strCreateTrigger, strVerbindungszeichenfolge, strFehlermeldung)\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> lngErgebnis = 0<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Debug.Print<\/span> lngErgebnis, strFehlermeldung\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         TriggerErstellen = <span style=\"color:blue;\">True<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 6: Funktion zum Anlegen des Triggers, der beim Aktualisieren oder L&ouml;schen eines Datensatzes ausgel&ouml;st werden soll.<\/span><\/b><\/p>\n<p>Das kann beispielsweise geschehen, wenn bereits ein gleichnamiger Trigger vorhanden ist. Sollte dies bei Ihnen vorkommen, k&ouml;nnen Sie folgendes Skript im Formular <b>frmSQLBefehle <\/b>ausf&uuml;hren, um die vorhandenen Trigger aufzulisten:<\/p>\n<pre>SELECT  \r\n     name,\r\n     is_instead_of_trigger\r\nFROM \r\n     sys.triggers  \r\nWHERE \r\n     type = 'TR';<\/pre>\n<p>Mit der <b>DROP<\/b>-Anweisung k&ouml;nnen Sie den Trigger dann l&ouml;schen:<\/p>\n<pre>DROP TRIGGER tblKunden_DeleteUpdate<\/pre>\n<p>Im Formular sieht dies dann wie in Bild 2 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_06\/pic_1212_002.png\" alt=\"Auflisten der Trigger im Access-Formular\" width=\"499,6607\" height=\"422,3323\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Auflisten der Trigger im Access-Formular<\/span><\/b><\/p>\n<p>Wenn Sie m&ouml;chten, k&ouml;nnen Sie die Prozedur zum Erstellen des Triggers auch direkt mit einer <b>DROP TRIGGER<\/b>-Anweisung ausstatten. Damit wird der zu erstellende Trigger vor dem Erstellen gel&ouml;scht.<\/p>\n<p>Allerdings l&ouml;st auch dies wieder einen Fehler aus. Daher pr&uuml;fen wir, ob der Trigger vorhanden ist, bevor wir diesen l&ouml;schen, und zwar mit folgendem T-SQL-Skript &#8211; hier f&uuml;r die Tabelle <b>tblKunden<\/b>:<\/p>\n<pre>IF EXISTS (SELECT 1 FROM sys.triggers WHERE Name = 'tblKunden_DeleteUpdate'\r\nBEGIN\r\n     DROP TRIGGER tblKunden_DeleteUpdate\r\nEND<\/pre>\n<p>Dieses f&uuml;gen wir wie in Listing 7 zur Funktion <b>TriggerErstellen <\/b>hinzu. Dazu verwenden wir eine Variable namens <b>strDropTrigger<\/b>, in der wir die T-SQL-Anweisungen speichern, wobei wir den Namen der Tabelle &uuml;ber die Variable <b>strTabelle <\/b>in die Bezeichnung f&uuml;r den zu pr&uuml;fenden und zu l&ouml;schenden Trigger integrieren.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>TriggerErstellen()\r\n     <span style=\"color:blue;\">Dim <\/span>strTabelle<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strCreateTrigger<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strFehlermeldung<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strDropTrigger<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngErgebnis<span style=\"color:blue;\"> As Long<\/span>\r\n     strTabelle = \"tblKunden\"\r\n     strVerbindungszeichenfolge = VerbindungszeichenfolgeNachID(10)\r\n     strDropTrigger = \"IF EXISTS (SELECT 1 FROM sys.triggers WHERE Name = '\" & strTabelle & \"_DeleteUpdate')\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strDropTrigger = strDropTrigger & \"BEGIN\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strDropTrigger = strDropTrigger & \"    DROP TRIGGER \" & strTabelle & \"_DeleteUpdate\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     strDropTrigger = strDropTrigger & \"END\" & <span style=\"color:blue;\">vbCrLf<\/span>\r\n     lngErgebnis = SQLAktionsabfrageOhneErgebnis(strDropTrigger, strVerbindungszeichenfolge, strFehlermeldung)\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> lngErgebnis = 0<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Debug.Print<\/span> lngErgebnis, strFehlermeldung\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         strCreateTrigger = CREATETRIGGERZusammenstellen(strTabelle, strVerbindungszeichenfolge)\r\n         lngErgebnis = SQLAktionsabfrageOhneErgebnis(strCreateTrigger, strVerbindungszeichenfolge, strFehlermeldung)\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> lngErgebnis = 0<span style=\"color:blue;\"> Then<\/span>\r\n             <span style=\"color:blue;\">Debug.Print<\/span> lngErgebnis, strFehlermeldung\r\n         <span style=\"color:blue;\">Else<\/span>\r\n             TriggerErstellen = <span style=\"color:blue;\">True<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 7: Funktion zum Anlegen des Triggers mit vorherigem L&ouml;schen, falls der Trigger bereits vorhanden ist<\/span><\/b><\/p>\n<p>Dann f&uuml;hren wir diese Anweisung mit der Funktion <b>SQLAktionsabfrageOhneErgebnis <\/b>aus. Ist der zur&uuml;ckgegebene Fehlercode gleich <b>0<\/b>, erstellen wir den Trigger anschlie&szlig;end mit den bereits bekannten Anweisungen neu.<\/p>\n<p><b>Archivtabelle bei Vorhandensein l&ouml;schen<\/b><\/p>\n<p>W&auml;hrend der Entwicklung der Routinen f&uuml;r diesen Beitrag mussten wir &ouml;fter die Archivtabelle l&ouml;schen, um diese anschlie&szlig;end neu erstellen zu k&ouml;nnen. Daher haben wir der Funktion <b>ArchivtabelleErstellen<\/b> noch Zeilen hinzugef&uuml;gt, mit denen wir die Tabelle, falls diese bereits vorhanden ist, gel&ouml;scht haben. Das ist nat&uuml;rlich nicht f&ouml;rderlich, wenn die Archivtabelle bereits Daten enth&auml;lt. Deshalb f&uuml;gen wir der nachfolgend beschriebenen Prozedur zum Aufrufen der Funktionen zum Anlegen von Archivtabelle und Trigger eine entsprechende Warnmeldung hinzu.<\/p>\n<p><b>Prozedur zum Aufrufen beider Routinen<\/b><\/p>\n<p>Wenn wir beide Schritte hintereinander durchf&uuml;hren wollen, also die Archivtabelle und den Trigger zu erstellen, k&ouml;nnen wir eine Prozedur wie die namens <b>ArchivtabelleUndTriggerErstellen <\/b>aus Listing 8 verwenden.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>ArchivtabelleUndTriggerErstellen()\r\n     <span style=\"color:blue;\">Dim <\/span>strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strTabelle<span style=\"color:blue;\"> As String<\/span>\r\n     strTabelle = \"tblKunden\"\r\n     strVerbindungszeichenfolge = VerbindungszeichenfolgeNachID(10)\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">MsgBox<\/span>(\"Achtung: Bereits vorhandene Archivtabelle wird gel&ouml;scht! Fortsetzen\", vbYesNo) = vbYes<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">If <\/span>ArchivtabelleErstellen(strTabelle, strVerbindungszeichenfolge) = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n             <span style=\"color:blue;\">If <\/span>TriggerErstellen(strTabelle, strVerbindungszeichenfolge) = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n                 <span style=\"color:blue;\">MsgBox<\/span> \"Archivtabelle f&uuml;r '\" & strTabelle & \"' und Trigger erfolgreich erstellt.\"\r\n             <span style=\"color:blue;\">Else<\/span>\r\n                 <span style=\"color:blue;\">MsgBox<\/span> \"Archivtabelle erstellt, Trigger jedoch nicht.\"\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">Else<\/span>\r\n             <span style=\"color:blue;\">MsgBox<\/span> \"Archivtabelle nicht erstellt.\"\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 8: Aufruf der Funktionen zum Anlegen von Archivtabelle und Trigger<\/span><\/b><\/p>\n<p>Um den Namen der Tabelle und die Verbindungszeichenfolge nur einmal festlegen zu m&uuml;ssen, haben wir diese in den beiden Funktionen <b>ArchivtabelleErstellen <\/b>und <b>TriggerErstellen <\/b>parametrisiert, sodass deren erste Zeilen wie folgt aussehen:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>ArchivtabelleErstellen(strTabelle<span style=\"color:blue;\"> As String<\/span>, strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As Boolean<\/span>\r\n...\r\n<span style=\"color:blue;\">Public Function <\/span>TriggerErstellen(strTabelle<span style=\"color:blue;\"> As String<\/span>, strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As Boolean<\/span>\r\n...<\/pre>\n<p>Den Namen der Tabelle, f&uuml;r die wir eine Archivtabelle anlegen wollen, und die Verbindungszeichenfolge brauchen wir dann nur noch in der Prozedur <b>ArchivtabelleUndTrigger-Erstellen <\/b>anzugeben. Diese Prozedur gibt dann zun&auml;chst eine Warnmeldung aus, damit der Benutzer nicht versehentlich eine bereits vorhandene Archivtabelle &uuml;berschreibt. Danach ruft sie erst die Funktion <b>ArchivtabelleErstellen <\/b>auf.<\/p>\n<p>Liefert diese den Wert <b>True <\/b>zur&uuml;ck, was geschieht, wenn die Anweisungen ohne Fehler ausgef&uuml;hrt wurden, wird auch noch die Funktion <b>TriggerErstellen <\/b>aufgerufen. Es gibt in jedem Fall eine Meldung &#8211; wenn Archivtabelle und Trigger erstellt wurden oder wenn nur die Archivtabelle erstellt wurde, der Trigger aber nicht oder wenn noch nicht einmal die Archivtabelle erstellt wurde.<\/p>\n<p><b>Archivtabellen komfortabel erstellen<\/b><\/p>\n<p>Schlie&szlig;lich wollen wir noch ein Formular erstellen, dass alle Tabellen der SQL Server-Datenbank anzeigt und die Auswahl der Tabellen erlaubt, die mit einer Archivtabelle und dem entsprechenden Trigger versehen werden sollen. Das Formular soll in einem Kombinationsfeld die Auswahl der zu verwendenden Verbindungszeichenfolge erlauben und danach alle enthaltenen Tabellen in einem Listenfeld anzeigen. Dabei sollen alle Tabellen, f&uuml;r die bereits eine Archivtabelle vorliegt, speziell markiert werden &#8211; beispielsweise durch ein <b>X <\/b>in der ersten Spalte des Eintrags im Listenfeld.<\/p>\n<p>Das Formular soll <b>frmArchivtabellenVerwalten <\/b>hei&szlig;en. Im oberen Bereich f&uuml;gen wir das bereits erw&auml;hnte Kombinationsfeld ein. Dieses k&ouml;nnen wir aus dem Formular <b>frmTabellenVerknuepfen <\/b>entnehmen, das zu den SQL Server Tools geh&ouml;rt. Au&szlig;erdem kopieren wir von dort die Schaltfl&auml;che <b>cmdVerbindungszeichenfolgen <\/b>mit der Ereignisprozedur, die das Formular <b>frmVerbindungszeichenfolgen <\/b>&ouml;ffnet.<\/p>\n<p>Dann wird es spannend: Nach der Auswahl einer Verbindung sollen die Tabellen der in dieser Verbindung angegebenen Datenbank in einem Listenfeld angezeigt werden &#8211; und die Tabellen, die bereits eine Archivtabelle haben, sollen mit einem f&uuml;hrenden <b>X <\/b>in der ersten Spalte markiert werden.<\/p>\n<p>Wir ermitteln die Tabellen der Datenbank ohne die Archivtabellen mit der folgenden Abfrage:<\/p>\n<pre>SELECT [Name] FROM sys.tables WHERE NOT [Name] LIKE '%_Archiv';<\/pre>\n<p>Die Archivtabellen liefert diese Abfrage:<\/p>\n<pre>SELECT [Name] FROM sys.tables WHERE [name] LIKE '%_Archiv';<\/pre>\n<p>Wie aber erhalten wir f&uuml;r die erste Abfrage ein zus&auml;tzliches Feld, das ein <b>X <\/b>enth&auml;lt, wenn es f&uuml;r die Tabelle schon eine Archivtabelle gibt Um f&uuml;r jeden Datensatz ein X auszugeben, k&ouml;nnen wir die folgende Abfrage einsetzen:<\/p>\n<pre>SELECT (SELECT 'X'), [Name] FROM sys.tables WHERE NOT [Name] LIKE '%_Archiv';<\/pre>\n<p>Nun wollen wir allerdings nur ein <b>X<\/b>, wenn es zum Tabellennamen auch einen Eintrag in der Tabelle mit dem Tabellennamen plus <b>_Archiv <\/b>gibt. Das erreichen wir mit der folgenden Abfrage:<\/p>\n<pre>SELECT (\r\n     SELECT 'X' FROM sys.tables AS t2 \r\n     WHERE t2.[name] LIKE '%_Archiv' \r\n     AND t1.[name] LIKE REPLACE(t2.[Name], '_Archiv', '')\r\n), t1.[Name] FROM sys.tables AS t1 WHERE NOT t1.[Name] LIKE '%_Archiv';<\/pre>\n<p>Wir ermitteln also in einer Unterabfrage ein <b>X<\/b>, wenn es einen Datensatz gibt, dessen Feld <b>Name <\/b>auf <b>_Archiv <\/b>endet und wenn der Name der Tabelle <b>sys.tables <\/b>der Hauptabfrage in der Tabelle der Unterabfrage mit angeh&auml;ngtem <b>_Archiv <\/b>auftaucht.<\/p>\n<p><b>Listenfeld mit Tabellen anzeigen<\/b><\/p>\n<p>Das Listenfeld, das das Ergebnis dieser Abfrage anzeigen soll, f&uuml;gen wir direkt unter dem Kombinationsfeld ein und nennen es <b>lstTabellen<\/b>.<\/p>\n<p>Es soll nach der Auswahl eines Eintrags des Kombinationsfeldes <b>cboVerbindung <\/b>gef&uuml;llt werden. Also legen wir f&uuml;r das Ereignis <b>Nach Aktualisierung <\/b>des Kombinationsfeldes die folgende Ereignisprozedur an, die wie in Listing 9 aussieht.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cboVerbindung_AfterUpdate()\r\n     <span style=\"color:blue;\">Dim <\/span>strSQL<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     strVerbindungszeichenfolge = VerbindungszeichenfolgeNachID(Me!cboVerbindung)\r\n     strSQL = \"SELECT (\" _\r\n         & \"    SELECT 'X' FROM sys.tables AS t2 \" _\r\n         & \"    WHERE t2.[name] LIKE '%_Archiv' \" _\r\n         & \"    AND t1.[name] LIKE REPLACE(t2.[Name], '_Archiv', '')\" _\r\n         & \"), t1.[Name] \" _\r\n         & \"FROM sys.tables AS t1 \" _\r\n         & \"WHERE NOT t1.[Name] LIKE '%_Archiv';\"\r\n     <span style=\"color:blue;\">Set<\/span> rst = SQLRecordset(strSQL, strVerbindungszeichenfolge)\r\n     <span style=\"color:blue;\">Set<\/span> Me!lstTabellen.Recordset = rst\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 9: Prozedur zum F&uuml;llen des Listenfeldes mit den Tabellen der Datenbank<\/span><\/b><\/p>\n<p>Die Prozedur ermittelt die Verbindungszeichenfolge zu der mit dem Kombinationsfeld ausgew&auml;hlten Verbindung und stellt in <b>strSQL <\/b>die oben beschriebene SQL-Abfrage zusammen. Dann &ouml;ffnet sie mit der Funktion <b>SQLRecordset <\/b>ein Recordset auf Basis der Verbindungszeichenfolge und der SQL-Abfrage und weist dieses der Eigenschaft <b>Recordset <\/b>des Listenfeldes zu.<\/p>\n<p>Damit beide Felder des Recordsets im Listenfeld angezeigt werden, stellen wir dessen Eigenschaften <b>Spaltenanzahl <\/b>auf <b>2 <\/b>und <b>Spaltenbreiten <\/b>auf <b>1cm <\/b>ein.<\/p>\n<p>Das Ergebnis sieht dann wie in Bild 3 aus, wenn wir nur f&uuml;r die Tabelle <b>tblKunden <\/b>eine Archivtabelle angelegt haben.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2019_06\/pic_1212_003.png\" alt=\"Formular mit gef&uuml;lltem Listenfeld\" width=\"499,6607\" height=\"269,5664\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Formular mit gef&uuml;lltem Listenfeld<\/span><\/b><\/p>\n<p><b>Tabellen ausw&auml;hlen und Archivtabellen anlegen<\/b><\/p>\n<p>Damit der Benutzer eine oder mehrere der Tabellen im Listenfeld ausw&auml;hlen kann, stellen wir die Eigenschaft <b>Mehrfachauswahl <\/b>auf <b>Erweitert <\/b>ein.<\/p>\n<p>Unter dem Listenfeld f&uuml;gen wir eine Schaltfl&auml;che namens <b>cmdArchivtabelleAnlegen <\/b>ein, f&uuml;r die wir eine Ereignisprozedur anlegen, welche f&uuml;r die ausgew&auml;hlten Tabellen Archivtabellen und die entsprechenden Trigger anlegen soll.<\/p>\n<p>Diese Prozedur sieht wie in Listing 10 aus. Die Prozedur durchl&auml;uft alle markierten Eintr&auml;ge des Listenfeldes und ermittelt dabei mit der <b>Column<\/b>-Eigenschaft den Wert der zweiten Spalte eines jeden Eintrags. Mit diesem und mit der Verbindungszeichenfolge ausgestattet ruft sie dann jeweils die Funktion <b>ArchivtabelleUndTriggerErstellen <\/b>auf. Nachdem alle Eintr&auml;ge durchlaufen wurden, erfolgt ein Aufruf der Prozedur <b>cboVerbindung_AfterUpdate<\/b>, wodurch die Tabellen neu eingelesen werden.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdArchivtabellenAnlegen_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>var<span style=\"color:blue;\"> As Variant<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strVerbindungszeichenfolge<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strTabelle<span style=\"color:blue;\"> As String<\/span>\r\n     strVerbindungszeichenfolge = VerbindungszeichenfolgeNachID(Me!cboVerbindung)\r\n     For Each var In Me!lstTabellen.ItemsSelected\r\n         strTabelle = Me!lstTabellen.Column(1, var)\r\n         <span style=\"color:blue;\">Debug.Print<\/span> ArchivtabelleUndTriggerErstellen(strTabelle, strVerbindungszeichenfolge)\r\n     <span style=\"color:blue;\">Next<\/span> var\r\n     cboVerbindung_AfterUpdate\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 10: Prozedur zum Anlegen der Archivtabellen zu den markierten Tabellen im Listenfeld<\/span><\/b><\/p>\n<p>Dabei erscheinen dann f&uuml;r die Tabellen, die erfolgreich mit einer Archivtabelle versehen wurden, ein <b>X<\/b>.<\/p>\n<p>Allerdings wird hier leider noch nicht deutlich, ob auch der Trigger erfolgreich angelegt wurde. Also f&uuml;gen wir der Datenherkunft des Listenfeldes noch ein Feld hinzu, das angibt, ob auch ein entsprechender Trigger vorliegt. Dieser sollte nach dem Schema <b><Tabellenname>_DeleteUpdate <\/b>benannt sein. Die Abfrage erweitern wir wie folgt:<\/p>\n<pre>SELECT (\r\n     SELECT 'X' FROM sys.tables AS t2\r\n         WHERE t2.[name] LIKE '%_Archiv'\r\n         AND t1.[name] LIKE REPLACE(t2.[Name], '_Archiv', '')\r\n     ), (\r\n         SELECT 'X' FROM sys.triggers AS t3\r\n         WHERE t3.[name] LIKE t1.Name + '_DeleteUpdate'\r\n), t1.[Name]\r\nFROM sys.tables AS t1 \r\nWHERE NOT t1.[Name] LIKE '%_Archiv';<\/pre>\n<p>Auch die Eigenschaften des Listenfeldes passen wir an, und zwar <b>Spaltenanzahl <\/b>auf <b>3 <\/b>und <b>Spaltenbreiten <\/b>auf <b>1cm;1cm<\/b>. So erkennen wir nun auf einen Blick, f&uuml;r welche Tabellen sowohl die Archivtabelle als auch der Trigger angelegt wurde. Au&szlig;erdem m&uuml;ssen wir in der Prozedur <b>cmdArchivtabellenAnlegen_Click<\/b> nun auf die dritte Spalte des Listenfeldes zugreifen (<b>Me!lstTabellen.Column(2, var)<\/b>).<\/p>\n<p><b>Meldungen minimieren<\/b><\/p>\n<p>Nun st&ouml;ren nur noch die vielen Meldungen, die erscheinen, wenn man Archivtabellen gleich f&uuml;r mehrere Tabellen anlegen m&ouml;chte. Dies &auml;ndern wir noch, indem wir die Warnmeldung aus der Funktion <b>ArchivtabelleUnd-Trigger-Erstellen <\/b>in die Ereignisprozedur <b>cmdArchivtabellenAnlegen_Click <\/b>vorziehen.<\/p>\n<p>Au&szlig;erdem wollen wir auch noch die Hinweise auf nicht erstellte Archivtabellen oder Trigger an anderer, zentralerer Stelle unterbringen, was wir in der Prozedur <b>cmdArchivtabellenAnlegen_Click <\/b>erledigt haben.<\/p>\n<p><b>Zusammenfassung und Ausblick<\/b><\/p>\n<p>Dieser Beitrag beschreibt eine L&ouml;sung, mit der Sie mit einem einfachen Formular Archivtabellen f&uuml;r Tabellen in SQL Server-Datenbanken hinzuf&uuml;gen k&ouml;nnen und gleichzeitig Trigger anlegen, die die letzte Version ge&auml;nderter oder gel&ouml;schter Datens&auml;tze in dieser Archivtabelle speichern.<\/p>\n<h3>Downloads zu diesem Beitrag<\/h3>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>DatenhistorieTriggerSchnellAnlegen.accdb<\/p>\n<p>Suedsturm_SQL.sql<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/6CA29E0A-D921-4962-B248-262F32C1A375\/aiu_1212.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Im Beitrag &#8222;Datenhistorie per Trigger&#8220; haben wir gezeigt, welche Anweisungen n&ouml;tig sind, um eine Archivtabelle anzulegen und der Originaltabelle einen Trigger hinzuzuf&uuml;gen, der beim &Auml;ndern oder L&ouml;schen eines Datensatzes die vorherige Version in einer Archivtabelle speichert. Im vorliegenden Beitrag wollen wir eine VBA-Prozedur entwickeln, mit der Sie zu einer Tabelle mit einem einfachen Aufruf die Archivtabelle und den Trigger in einem Rutsch anlegen k&ouml;nnen. Damit sichern Sie Ihre Daten bei &Auml;nderungen auch f&uuml;r mehrere Tabellen ganz schnell ab.<\/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-55001212","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.4) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Datenhistorie-Trigger schnell anlegen - 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\/DatenhistorieTrigger_schnell_anlegen\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Datenhistorie-Trigger schnell anlegen\" \/>\n<meta property=\"og:description\" content=\"Im Beitrag &quot;Datenhistorie per Trigger&quot; haben wir gezeigt, welche Anweisungen n&ouml;tig sind, um eine Archivtabelle anzulegen und der Originaltabelle einen Trigger hinzuzuf&uuml;gen, der beim &Auml;ndern oder L&ouml;schen eines Datensatzes die vorherige Version in einer Archivtabelle speichert. Im vorliegenden Beitrag wollen wir eine VBA-Prozedur entwickeln, mit der Sie zu einer Tabelle mit einem einfachen Aufruf die Archivtabelle und den Trigger in einem Rutsch anlegen k&ouml;nnen. Damit sichern Sie Ihre Daten bei &Auml;nderungen auch f&uuml;r mehrere Tabellen ganz schnell ab.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/DatenhistorieTrigger_schnell_anlegen\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2020-07-10T09:44:10+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg05.met.vgwort.de\/na\/d7f310363c674b64906407880e2adade\" \/>\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=\"21\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/DatenhistorieTrigger_schnell_anlegen\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/DatenhistorieTrigger_schnell_anlegen\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Datenhistorie-Trigger schnell anlegen\",\"datePublished\":\"2020-07-10T09:44:10+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/DatenhistorieTrigger_schnell_anlegen\\\/\"},\"wordCount\":2986,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/DatenhistorieTrigger_schnell_anlegen\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg05.met.vgwort.de\\\/na\\\/d7f310363c674b64906407880e2adade\",\"articleSection\":[\"2019\",\"6\\\/2019\",\"SQL Server und Co.\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/DatenhistorieTrigger_schnell_anlegen\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/DatenhistorieTrigger_schnell_anlegen\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/DatenhistorieTrigger_schnell_anlegen\\\/\",\"name\":\"Datenhistorie-Trigger schnell anlegen - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/DatenhistorieTrigger_schnell_anlegen\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/DatenhistorieTrigger_schnell_anlegen\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg05.met.vgwort.de\\\/na\\\/d7f310363c674b64906407880e2adade\",\"datePublished\":\"2020-07-10T09:44:10+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/DatenhistorieTrigger_schnell_anlegen\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/DatenhistorieTrigger_schnell_anlegen\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/DatenhistorieTrigger_schnell_anlegen\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg05.met.vgwort.de\\\/na\\\/d7f310363c674b64906407880e2adade\",\"contentUrl\":\"http:\\\/\\\/vg05.met.vgwort.de\\\/na\\\/d7f310363c674b64906407880e2adade\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/DatenhistorieTrigger_schnell_anlegen\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Datenhistorie-Trigger schnell anlegen\"}]},{\"@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-Trigger schnell anlegen - 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\/DatenhistorieTrigger_schnell_anlegen\/","og_locale":"de_DE","og_type":"article","og_title":"Datenhistorie-Trigger schnell anlegen","og_description":"Im Beitrag \"Datenhistorie per Trigger\" haben wir gezeigt, welche Anweisungen n&ouml;tig sind, um eine Archivtabelle anzulegen und der Originaltabelle einen Trigger hinzuzuf&uuml;gen, der beim &Auml;ndern oder L&ouml;schen eines Datensatzes die vorherige Version in einer Archivtabelle speichert. Im vorliegenden Beitrag wollen wir eine VBA-Prozedur entwickeln, mit der Sie zu einer Tabelle mit einem einfachen Aufruf die Archivtabelle und den Trigger in einem Rutsch anlegen k&ouml;nnen. Damit sichern Sie Ihre Daten bei &Auml;nderungen auch f&uuml;r mehrere Tabellen ganz schnell ab.","og_url":"https:\/\/access-im-unternehmen.de\/DatenhistorieTrigger_schnell_anlegen\/","og_site_name":"Access im Unternehmen","article_published_time":"2020-07-10T09:44:10+00:00","og_image":[{"url":"http:\/\/vg05.met.vgwort.de\/na\/d7f310363c674b64906407880e2adade","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"21\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/DatenhistorieTrigger_schnell_anlegen\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/DatenhistorieTrigger_schnell_anlegen\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Datenhistorie-Trigger schnell anlegen","datePublished":"2020-07-10T09:44:10+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/DatenhistorieTrigger_schnell_anlegen\/"},"wordCount":2986,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/DatenhistorieTrigger_schnell_anlegen\/#primaryimage"},"thumbnailUrl":"http:\/\/vg05.met.vgwort.de\/na\/d7f310363c674b64906407880e2adade","articleSection":["2019","6\/2019","SQL Server und Co."],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/DatenhistorieTrigger_schnell_anlegen\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/DatenhistorieTrigger_schnell_anlegen\/","url":"https:\/\/access-im-unternehmen.de\/DatenhistorieTrigger_schnell_anlegen\/","name":"Datenhistorie-Trigger schnell anlegen - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/DatenhistorieTrigger_schnell_anlegen\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/DatenhistorieTrigger_schnell_anlegen\/#primaryimage"},"thumbnailUrl":"http:\/\/vg05.met.vgwort.de\/na\/d7f310363c674b64906407880e2adade","datePublished":"2020-07-10T09:44:10+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/DatenhistorieTrigger_schnell_anlegen\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/DatenhistorieTrigger_schnell_anlegen\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/DatenhistorieTrigger_schnell_anlegen\/#primaryimage","url":"http:\/\/vg05.met.vgwort.de\/na\/d7f310363c674b64906407880e2adade","contentUrl":"http:\/\/vg05.met.vgwort.de\/na\/d7f310363c674b64906407880e2adade"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/DatenhistorieTrigger_schnell_anlegen\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Datenhistorie-Trigger schnell anlegen"}]},{"@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\/55001212","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=55001212"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001212\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001212"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001212"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001212"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}