{"id":55000925,"date":"2014-04-01T00:00:00","date_gmt":"2020-05-22T21:19:28","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=925"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Geaenderte_Daten_archivieren","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Geaenderte_Daten_archivieren\/","title":{"rendered":"Ge&auml;nderte Daten archivieren"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg07.met.vgwort.de\/na\/9eacc3746a4a475da7befb79424b9aeb\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>Das &auml;ndern von Daten ist in manchen F&auml;llen an der Tagesordnung &#8211; zum Beispiel bei Kundendaten. Wenn Sie in einer Datenbank Kunden verwalten, die nicht nur f&uuml;r einen einzigen Auftrag oder eine einzige Bestellung angelegt werden, sondern mit denen Sie wiederkehrend gesch&auml;ftlichen Kontakt haben, m&uuml;ssen Sie die Adressdaten auf dem aktuellen Stand halten. Kein Problem: Die entsprechenden Felder lassen sich ja schnell mal &uuml;berschreiben. Das Problem ist nur, dass hier auch Fehler geschehen. Wenn Sie beispielsweise beim falschen Michael M&uuml;ller die Lieferadresse &auml;ndern, freut sich ein Michael M&uuml;ller &uuml;ber eine Lieferung, aber der andere, der nichts bestellt hat, wird die Rechnung wohl nicht bezahlen. Und wenn sich solche Fehler nur schwer verhindern lassen, so sollen Sie zumindest die Adressdaten schnell wiederherstellen k&ouml;nnen &#8230;<\/b><\/p>\n<p>Und genau darum k&uuml;mmert sich der vorliegende Beitrag: Wir entwickeln hier eine automatische L&ouml;sung zum Archivieren von ge&auml;nderten oder gel&ouml;schten Datens&auml;tzen einer Kundendatenbank. Au&szlig;erdem wollen wir ein Formular bereitstellen, mit dem Sie den aktuellen und die bereits archivierten Datens&auml;tze zu einem Kunden einsehen, vergleichen und beliebige Versionsst&auml;nde wiederherstellen k&ouml;nnen.<\/p>\n<p><b>Voraussetzung<\/b><\/p>\n<p>Die Sache hat einen kleinen Haken: Die L&ouml;sung basiert auf der Verwendung von Access 2010 oder h&ouml;her. Der Grund ist, dass wir hier mit den Tabellenereignissen arbeiten, die erst mit Access 2010 eingef&uuml;hrt wurden. Tabellenereignisse sind das Access-Pendant etwa zu den Triggern beim SQL Server. Dabei handelt es sich um ereignisgesteuerte, benutzerdefinierte Aktionen, die durch das Anlegen, Bearbeiten oder L&ouml;schen von Daten ausgel&ouml;st werden.<\/p>\n<p>In unserem Fall spielt das Anlegen keine Rolle: Wir wollen nur die M&ouml;glichkeit liefern, ge&auml;nderte oder gel&ouml;schte Daten wiederherzustellen. Dazu ben&ouml;tigen wir kein Anlegedatum oder &auml;hnliche Informationen. Also werden wir sp&auml;ter nur ein Ereignis definieren, das beim &auml;ndern eines Datensatzes der betroffenen Tabelle ausgel&ouml;st wird sowie eines f&uuml;r das L&ouml;schen eines Datensatzes der Tabelle.<\/p>\n<p><b>Beispieltabelle<\/b><\/p>\n<p>Als Beispieltabelle verwenden wir eine einfache Kundentabelle. Diese sieht im Entwurf wie in Bild 1 aus und enth&auml;lt nur die notwendigsten Felder.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_001.png\" alt=\"Tabelle, deren Daten beim &auml;ndern archiviert werden sollen\" width=\"450\" height=\"383,7793\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Tabelle, deren Daten beim &auml;ndern archiviert werden sollen<\/span><\/b><\/p>\n<p><b>Zieltabelle der Archivierung<\/b><\/p>\n<p>Das Ziel der Archivierung ist eine Tabelle, die fast genauso wie die Originaltabelle aufgebaut ist. Es gibt allerdings einige kleine Unterschiede: Die Zieltabelle verwendet das Feld <b>KundeID <\/b>n&auml;mlich nicht als Prim&auml;rschl&uuml;sselfeld mit dem Felddatentyp <b>Autowert<\/b>, sondern als einfaches Feld mit dem Typ <b>Zahl<\/b>. Als Prim&auml;rschl&uuml;sselfeld definieren wir ein weiteres Feld namens <b>ArchivKundeID<\/b>. Dies legen wir wiederum als Autowertfeld aus (s. Bild 2).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_002.png\" alt=\"Tabelle zum Archivieren von Daten vor dem &auml;ndern oder L&ouml;schen\" width=\"450\" height=\"457,9787\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Tabelle zum Archivieren von Daten vor dem &auml;ndern oder L&ouml;schen<\/span><\/b><\/p>\n<p>Au&szlig;erdem finden Sie am Ende des Entwurfs der Tabelle zwei weitere Felder namens <b>GeaendertAm <\/b>und <b>Geloescht-Am <\/b>mit dem Datentyp <b>Datum\/Uhrzeit<\/b>. Wenn der Benutzer einen Datensatz der Tabelle <b>tblKunden <\/b>&auml;ndert, soll die Version des Datensatzes vor der &auml;nderung in die Tabelle <b>tblKundenArchiv <\/b>kopiert werden und das aktuelle Datum im Feld <b>GeaendertAm <\/b>erhalten. Wenn der Benutzer hingegen einen Datensatz l&ouml;scht, landet dieser zwar auch komplett in der Tabelle <b>tblKundenArchiv <\/b>&#8211; diesmal allerdings mit dem aktuellen Datum im Feld <b>GeloeschtAm<\/b>.<\/p>\n<p><b>Makro zum Archivieren eines ge&auml;nderten Datensatzes<\/b><\/p>\n<p>Damit ein ge&auml;nderter Datensatz vor der &auml;nderung in der Archivtabelle landet, legen Sie ein Makro f&uuml;r das Ereignis <b>Nach Aktualisierung <\/b>der Tabelle an. Dies erledigen Sie durch einen Mausklick auf den Ribbon-Eintrag <b>Tabelle|Nachfol-ge-er-eignisse|Nach Aktualisierung<\/b> (s. Bild 3), w&auml;hrend die Tabelle in der Datenblattansicht ge&ouml;ffnet ist.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_003.png\" alt=\"Anlegen des Makros f&uuml;r das Ereignis Nach Aktualisierung der Tabelle ausgel&ouml;st\" width=\"575\" height=\"231,7381\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Anlegen des Makros f&uuml;r das Ereignis Nach Aktualisierung der Tabelle ausgel&ouml;st<\/span><\/b><\/p>\n<p>In der Entwurfsansicht legen Sie das entsprechende Makro &uuml;ber den Ribbon-Eintrag <b>Entwurf|Feld-, Datensatz- und Ta-bel-len-er-eig-nis-se|Da-ten-mak-ros erstellen|Nach Ak-tu-alisierung<\/b> an.<\/p>\n<p>Daraufhin erscheint ein leeres Makro, das Sie nun mit Leben f&uuml;llen m&uuml;ssen. Das gelingt relativ schnell:  Als Erstes w&auml;hlen Sie mit dem Kombinationsfeld <b>Neue Aktion hinzuf&uuml;gen <\/b>den Eintrag <b>Datensatz erstellen in <\/b>aus und tragen als ersten Parameter den Namen der Zieltabelle ein, also <b>tblKundenArchiv<\/b>. Nun m&uuml;ssen Sie festlegen, wie die einzelnen Felder dieser Tabelle gef&uuml;llt werden &#8211; und womit. Die ben&ouml;tigte Makroaktion ist schnell gefunden: Sie hei&szlig;t <b>FestlegenFeld <\/b>und kann nur angelegt werden, wenn Sie die Makroaktion <b>Datensatz erstellen in <\/b>markieren und dann das zu dieser Makroaktion geh&ouml;rende Kombinationsfeld <b>Neue Aktion hinzuf&uuml;gen <\/b>aufklappen.<\/p>\n<p>Haben Sie diese Aktion hinzugef&uuml;gt, geben Sie die beiden Parameter ein. Wir beginnen mit dem Feld <b>KundeID<\/b>, welches in das gleichnamige Feld der Tabelle <b>tblKundenArchiv <\/b>eingef&uuml;gt werden soll. Dazu tragen Sie f&uuml;r den Parameter <b>Name <\/b>einfach den Feldnamen <b>KundeID <\/b>ein. Das Makro wei&szlig; nun automatisch, dass das Feld <b>KundeID <\/b>der Tabelle <b>tblKundenArchiv <\/b>gef&uuml;llt werden soll. Aber woher beziehen wir den Inhalt f&uuml;r das Feld <b>KundeID<\/b> Wenn Sie dort ebenfalls den Eintrag <b>KundeID <\/b>hinzuf&uuml;gen, greift Access auf den Inhalt des Feldes des ge&auml;nderten Datensatzes zu. Wenn Sie auf den Feldinhalt des Datensatzes vor der &auml;nderung zugreifen m&ouml;chten, m&uuml;ssen Sie auf die generische Tabelle namens <b>Alt <\/b>zugreifen. F&uuml;r den vorherigen Wert des Feldes <b>KundeID <\/b>verwenden Sie also etwa den Ausdruck <b>[Alt].[KundeID]<\/b>. Gut: Beim Feld <b>Kunde-ID  <\/b>h&auml;tten Sie auch auf den Wert des ge&auml;nderten Datensatzes zugreifen k&ouml;nnen, denn das Prim&auml;rschl&uuml;sselfeld wird ja in der Regel nicht ge&auml;ndert &#8211; schon gar nicht, wenn es als Autowert definiert ist. Aber bei den anderen, tendenziell &auml;nderbaren Feldern, macht der Zugriff auf die mit <b>Alt <\/b>referenzierten vorherigen Werte durchaus Sinn. Dementsprechend f&uuml;gen Sie dem Makro weitere <b>FestlegenFeld<\/b>-Aktionen hinzu, welche Feld f&uuml;r Feld die Inhalte der Tabelle <b>Alt <\/b>in die Tabelle <b>tblKundenArchiv <\/b>&uuml;bertr&auml;gt.<\/p>\n<p>Schlie&szlig;lich fehlt noch die F&uuml;llung f&uuml;r das Feld <b>GeaendertAm<\/b>. Hier tragen Sie einfach die Funktion <b>Jetzt() <\/b>ein. Das komplette Makro finden Sie in Bild 4.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_004.png\" alt=\"Makro, das durch das Ereignis Nach Aktualisierung ausgel&ouml;st wird\" width=\"400\" height=\"795,585\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Makro, das durch das Ereignis Nach Aktualisierung ausgel&ouml;st wird<\/span><\/b><\/p>\n<p><b>Makro beim L&ouml;schen<\/b><\/p>\n<p>Das Makro, das beim L&ouml;schen eines Datensatzes der Tabelle <b>tblKunden <\/b>ausgel&ouml;st werden soll, sieht &auml;hnlich aus. Der einzige Unterschied ist, dass es nicht das Feld <b>GeaendertAm<\/b>, sondern <b>GeloeschtAm<\/b> mit dem aktuellen Datum f&uuml;llt (s. Bild 5). Und nat&uuml;rlich legen Sie es &uuml;ber einen anderen Ribbon-Eintrag an, n&auml;mlich <b>Tabelle|Nachfolgeereignis-se|-Nach L&ouml;schen<\/b>. Den Zugriff auf die Feldinhalte des gel&ouml;schten Datensatzes liefert wiederum die Tabelle <b>Alt<\/b>.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_005.png\" alt=\"Makro, das durch das Ereignis Nach L&ouml;schung ausgel&ouml;st wird\" width=\"450\" height=\"395,6568\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Makro, das durch das Ereignis Nach L&ouml;schung ausgel&ouml;st wird<\/span><\/b><\/p>\n<p><b>Probieren geht &uuml;ber studieren<\/b><\/p>\n<p>Nun m&uuml;ssen wir die beiden Makros noch ausprobieren. Legen Sie also einen neuen Datensatz in der Tabelle an. Nach dem &auml;ndern des Feldes <b>Vorname <\/b>und dem Speichern des Datensatzes in der Tabelle <b>tblKunden <\/b>finden Sie in der Tabelle <b>tblKundenArchiv <\/b>einen neuen Datensatz vor, der den Datensatz in der Form vor der &auml;nderung enth&auml;lt &#8211; samt &auml;nderungsdatum.<\/p>\n<p>Wenn Sie den Datensatz auch noch aus der Tabelle <b>tblKunden <\/b>l&ouml;schen, verschwindet dieser zwar aus dieser Tabelle, aber eine Kopie dieses Datensatzes landet in der Tabelle <b>tblKundenArchiv <\/b>&#8211; samt aktuellem Datum im Feld <b>GeloeschtAm<\/b>.<\/p>\n<p>Die Tabelle <b>tblKundenArchiv <\/b>sieht danach beispielsweise wie in Bild 6 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_006.png\" alt=\"Die Tabelle tblKundenArchiv mit einem ge&auml;nderten und einem gel&ouml;schten Datensatz\" width=\"700\" height=\"125,817\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Die Tabelle tblKundenArchiv mit einem ge&auml;nderten und einem gel&ouml;schten Datensatz<\/span><\/b><\/p>\n<p><b>Datenarchiv im Formular<\/b><\/p>\n<p>Nun hilft diese Archivierung nicht viel, wenn man es nicht entsprechend einsehen und nutzen kann &#8211; zum Beispiel, um einen bestimmten Stand wiederherzustellen. Dazu ben&ouml;tigen wir ein Formular, das sowohl den aktuellen Stand des Datensatzes liefert als auch die archivierten Versionen des Datensatzes. <\/p>\n<p>Am &uuml;bersichtlichsten ist vermutlich eine Darstellung der Datens&auml;tze in der Datenblattansicht. Bevor wir uns an die Umsetzung begeben, wollen wir uns noch &uuml;berlegen, welche M&ouml;glichkeiten uns diese liefern soll.<\/p>\n<p>Sollen Datens&auml;tze direkt in der Datenblattansicht ge&auml;ndert werden k&ouml;nnen Nein. Es soll nur m&ouml;glich sein, eine beliebige archivierte Version eines Datensatzes wiederherzustellen &#8211; egal, ob der Datensatz wegen einer &auml;nderung oder wegen einer L&ouml;schung in der Tabelle <b>tblKundenArchiv <\/b>gespeichert wurde.<\/p>\n<p>Wir k&ouml;nnten nun zwei Datenbl&auml;tter &uuml;bereinander darstellen, indem wir diese in zwei Unterformularen unterbringen. Das Problem dabei ist wie immer, dass die Datenbl&auml;tter in den beiden Unterformularen m&ouml;glichst synchron gehandhabt werden sollten &#8211; wenn der Benutzer das obere Datenblatt scrollt, sollte dies auch mit dem unteren geschehen, und wenn die Spaltenbreiten in einer von beiden Ansichten ge&auml;ndert werden, sollte dies auch f&uuml;r das jeweils andere Datenblatt durchgef&uuml;hrt werden.<\/p>\n<p><b>Datenherkunft des Unterformulars<\/b><\/p>\n<p>Da wir aber soeben festgelegt haben, dass wir ohnehin nicht direkt &auml;nderungen am aktuellen Datensatz vornehmen wollen und auch nicht an den archivierten Datens&auml;tzen, k&ouml;nnen wir auch den aktuellen Datensatz und die archivierten Datens&auml;tze gemeinsam in einem Unterformular in der Datenblattansicht unterbringen.<\/p>\n<p>Dies gelingt ganz einfach durch Zusammenf&uuml;hren des aktuellen Datensatzes aus der Tabelle <b>tblKunden <\/b>und der archivierten Datens&auml;tze aus der Tabelle <b>tblKundenArchiv <\/b>per <b>UNION<\/b>-Abfrage.<\/p>\n<p>Dies ist auch dann m&ouml;glich, wenn der Datensatz gar nicht mehr in der Tabelle <b>tblKunden <\/b>enthalten ist &#8211; der erste Teil der <b>UNION<\/b>-Abfrage liefert dann halt keine Daten.<\/p>\n<p>Schauen wir uns zun&auml;chst den Teil der <b>UNION<\/b>-Abfrage an, der den Teil der Datens&auml;tze beisteuert, die aus der Tabelle <b>tblKundenArchiv <\/b>stammen.<\/p>\n<p>Diese sollen wie in Bild 7 daherkommen, wo eine Abfrage namens <b>qryKundenArchiv<\/b> neben den eigentlichen Feldern der Tabelle <b>tblKundenArchiv <\/b>noch zwei berechnete Felder liefert.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_007.png\" alt=\"Die Abfrage qryKundenArchiv liefert die Daten der Tabelle tblKundenArchiv und bereitet diese auf.\" width=\"700\" height=\"150,2146\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 7: Die Abfrage qryKundenArchiv liefert die Daten der Tabelle tblKundenArchiv und bereitet diese auf.<\/span><\/b><\/p>\n<p>Das erste hei&szlig;t Aktion und soll den Wert <b>Gel&ouml;scht am: <\/b>enthalten, wenn das Feld <b>GeloeschtAm <\/b>des Datensatzes der Tabelle <b>tblKundenArchiv <\/b>einen Wert enth&auml;lt (s. Bild 8). Wenn hingegen das Feld <b>GeaendertAm <\/b>gef&uuml;llt ist, soll das Feld <b>Aktion <\/b>den Wert <b>Ge&auml;ndert am: <\/b>anzeigen. Der Ausdruck f&uuml;r dieses Feld sieht so aus:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_008.png\" alt=\"Die Abfrage qryKundenArchiv in der Entwurfsansicht\" width=\"700\" height=\"307,4324\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 8: Die Abfrage qryKundenArchiv in der Entwurfsansicht<\/span><\/b><\/p>\n<pre>Aktion: Wenn(IstNull([GeaendertAm]);\"Gel&ouml;scht\";\"Ge&auml;ndert\")<\/pre>\n<p>Das zweite berechnete Feld hei&szlig;t <b>Archivdatum <\/b>und soll entweder den Inhalt des Feldes <b>GeaendertAm <\/b>oder <b>GeloeschtAm <\/b>aufnehmen &#8211; je nachdem, welches der beiden einen Wert enth&auml;lt. Dieses Feld enth&auml;lt den folgenden Ausdruck:<\/p>\n<pre>Archivdatum: Wenn(IstNull([GeaendertAm]);[GeloeschtAm];[GeaendertAm])<\/pre>\n<p>Au&szlig;erdem soll die Abfrage absteigend nach dem Wert dieses Feldes sortieren. Nun wollen wir diese Abfrage mit dem betroffenen Datensatz der Tabelle <b>tblKunden <\/b>zusammenf&uuml;hren. Dazu legen Sie eine <b>UNION<\/b>-Abfrage wie die aus Bild 9 an.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_009.png\" alt=\"UNION-Abfrage\" width=\"575\" height=\"252,6855\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 9: UNION-Abfrage<\/span><\/b><\/p>\n<p>Damit die Spalte <b>Aktion <\/b>wie in Bild 10 den Wert <b>Aktuelle Version <\/b>f&uuml;r den aktuellen Datensatz anzeigt, definieren wir das zweite Feld der <b>UNION<\/b>-Abfrage mit <b>&#8220;Aktuelle Version&#8220; AS Aktion<\/b>.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_010.png\" alt=\"Die Abfrage f&uuml;hrt den aktuellen Datensatz und seine archivierten Versionen zusammen.\" width=\"700\" height=\"197,3611\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 10: Die Abfrage f&uuml;hrt den aktuellen Datensatz und seine archivierten Versionen zusammen.<\/span><\/b><\/p>\n<p>Das Feld <b>Archivdatum <\/b>des aktuellen Datensatzes soll, damit dieser immer ganz oben angezeigt wird, mit dem aktuellen Datum und der aktuellen Uhrzeit gef&uuml;llt werden. Dies erreichen wir mit dem Ausdruck <b>Now AS Archivdatum<\/b>.<\/p>\n<p>Bevor wir diese Abfrage nun als Datenherkunft des Unterformulars verwenden, m&uuml;ssen wir diesem noch einen Parameter hinzuf&uuml;gen, der als Vergleichswert eines <b>WHERE<\/b>-Kriteriums dient und nur diejenigen Datens&auml;tze zur&uuml;ckliefert, die dem zu untersuchenden Datensatz entsprechen.<\/p>\n<p>Der Parameter soll <b>prmKundeID <\/b>hei&szlig;en und wird in jede <b>SELECT<\/b>-Anweisung der Abfrage je einmal eingef&uuml;gt:<\/p>\n<pre>SELECT 0 AS ArchivKundeID, \r\n''Aktuelle Version'' AS Aktion, \r\nNow AS Archivdatum, KundeID, \r\nAnredeID, Vorname, Nachname, Firma, Strasse, PLZ, Ort, Land\r\nFROM tblKunden \r\nWHERE KundeID = [prmKundeID];\r\nUNION SELECT ArchivKundeID, Aktion, \r\nArchivdatum, KundeID, AnredeID, \r\nVorname, Nachname, Firma, Strasse, \r\nPLZ, Ort, Land \r\nFROM qryKundenArchiv \r\nWHERE KundeID = [prmKundeID]\r\nORDER BY Archivdatum;<\/pre>\n<p><b>Unterformular zur Anzeige der Datens&auml;tze<\/b><\/p>\n<p>Nun erstellen wir zun&auml;chst das Unterformular <b>sfmArchivierteDatensaetze<\/b>. Diesem weisen wir zun&auml;chst die Abfrage <b>qryKundenArchiv <\/b>als Datenherkunft zu, da dieses alle Felder enth&auml;lt, die sp&auml;ter im Unterformular angezeigt werden sollen. Sp&auml;ter, wenn der Benutzer das Formular &ouml;ffnet, soll dieses mit dem Wert des Feldes <b>KundeID <\/b>des zu untersuchenden Datensatzes der Tabelle <b>tblKunden <\/b>als Parameter ge&ouml;ffnet werden, woraufhin das Unterformular mit einem Recordset auf Basis der Abfrage <b>qryKundenArchivUnion <\/b>gef&uuml;llt wird, das zuvor bereits mit dem entsprechenden Parameter versehen wurde. Das Unterformular sieht nach dem Hinzuf&uuml;gen der entsprechenden Felder wie in Bild 11 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_011.png\" alt=\"Das Unterformular sfmArchivierteDatensaetze, hier zun&auml;chst mit der Abfrage qryKundenArchiv als Datenherkunft\" width=\"700\" height=\"404,5082\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 11: Das Unterformular sfmArchivierteDatensaetze, hier zun&auml;chst mit der Abfrage qryKundenArchiv als Datenherkunft<\/span><\/b><\/p>\n<p>Damit k&ouml;nnen wir uns nun gleich an die Erstellung des Hauptformulars begeben, das &#8211; mit integriertem Unterformular &#8211; im Entwurf wie in Bild 12 aussehen soll. Das Unterformular f&uuml;gen Sie dem Hauptformular hinzu, indem Sie es einfach aus dem Navigationsbereich in den Detailbereich des Hauptformulars ziehen. Anschlie&szlig;end stellen Sie die Eigenschaften <b>Horizontaler Anker <\/b>und <b>Vertikaler Anker <\/b>jeweils auf <b>Beide <\/b>ein, damit sich das Unterformular beim Vergr&ouml;&szlig;ern des Hauptformulars anpasst.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_012.png\" alt=\"Haupt- und Unterformular in der Entwurfsansicht\" width=\"700\" height=\"521,836\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 12: Haupt- und Unterformular in der Entwurfsansicht<\/span><\/b><\/p>\n<p>Au&szlig;erdem f&uuml;gen Sie im Hauptformular eine Schaltfl&auml;che namens <b>cmdWiederherstellen <\/b>ein, mit welcher der Benutzer den aktuell markierten Datensatz wiederherstellt und somit den bestehenden Datensatz &uuml;berschreibt beziehungsweise einen gel&ouml;schten Datensatz wiederherstellt.<\/p>\n<p><b>Unterformular filtern<\/b><\/p>\n<p><!--30percent--><\/p>\n<p>Zuvor m&uuml;ssen wir allerdings noch sicherstellen, dass das Unterformular auch nur die betroffenen Datens&auml;tze anzeigt. Deshalb erstellen wir noch eine &auml;hnliche Kombination aus Haupt- und Unterformular, die im Unterformular alle Kunden aus der Tabelle <b>tblKunden <\/b>anzeigt und mit <b>cmdHistorie <\/b>eine Schaltfl&auml;che enth&auml;lt, mit der Sie die Historie f&uuml;r den aktuell im Unterformular ausgew&auml;hlten Kunden im Formular <b>frmArchivierteDatensaetze <\/b>anzeigen k&ouml;nnen (s. Bild 13).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_013.png\" alt=\"Anzeige aller Kunden und Aufrufen der Historie eines Kunden\" width=\"600\" height=\"461,1307\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 13: Anzeige aller Kunden und Aufrufen der Historie eines Kunden<\/span><\/b><\/p>\n<p>Die Schaltfl&auml;che <b>cmdHistorie <\/b>l&ouml;st die Prozedur aus Listing 1 aus, die das Formular <b>frmArchivierteDatensaetze <\/b>&ouml;ffnet und mit dem &ouml;ffnungsargument den Wert des Feldes <b>KundeID <\/b>des aktuell ausgew&auml;hlten Datensatzes &uuml;bergibt. Diese ist dann im Klassenmodul des Formulars <b>frmArchivierteDatensaetze <\/b>&uuml;ber die Eigenschaft <b>Me.OpenArgs <\/b>verf&uuml;gbar und kann als Parameter der dem Unterformulars dieses Formulars zugrunde liegenden Abfrage verwendet werden.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdHistorie_Click()\r\n     DoCmd.OpenForm \"frmArchivierteDatensaetze\", OpenArgs:=Me!sfmArchivierteDatensaetze.Form!kundeID\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Diese Prozedur &ouml;ffnet das Formular sfmArchivierteDatensaetze<\/span><\/b><\/p>\n<p><b>Kunde mit Archiv anzeigen<\/b><\/p>\n<p>Nun m&uuml;ssen wir das Hauptformular <b>frmArchivierteDatensaetze <\/b>noch mit einer Ereignisprozedur ausstatten, die beim Laden das &ouml;ffnungsargument auswertet und die Datens&auml;tze zum richtigen Kunden im Unterformular anzeigt.<\/p>\n<p>Diese sieht wie in Listing 2 aus. Die Prozedur pr&uuml;ft, ob <b>Me.OpenArgs <\/b>&uuml;berhaupt einen Wert enth&auml;lt. Falls ja, f&uuml;llt die Prozedur die Variable <b>db <\/b>mit einem Verweis auf das aktuelle <b>Database<\/b>-Objekt. Die Variable <b>qdf <\/b>referenziert dann die Abfrage <b>qryKundenArchivUNION<\/b>, der mit der folgenden Anweisung der Parameter <b>prmKundeID <\/b>zugewiesen wird.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>qdf<span style=\"color:blue;\"> As <\/span>DAO.QueryDef\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>prm<span style=\"color:blue;\"> As <\/span>DAO.Parameter\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> IsNull(Me.OpenArgs)<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n         <span style=\"color:blue;\">Set<\/span> qdf = db.QueryDefs(\"qryKundenArchivUNION\")\r\n         <span style=\"color:blue;\">Set<\/span> prm = qdf.Parameters(\"prmKundeID\")\r\n         prm.Value = Me.OpenArgs\r\n         <span style=\"color:blue;\">Set<\/span> rst = qdf.OpenRecordset\r\n         <span style=\"color:blue;\">Set<\/span> Me!sfmArchivierteDatensaetze.Form.Recordset = rst\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 2: Ermitteln und Zuweisen der Datenherkunft des Unterformulars beim Laden<\/span><\/b><\/p>\n<p>Nachdem dieser den Wert erhalten hat, den <b>Me.OpenArgs <\/b>liefert, kann die Prozedur ein <b>Recordset<\/b>-Objekt auf Basis dieser Abfrage mit dem gegebenen Parameter erstellen und dieses dem Unterformular als Datenherkunft zuweisen.<\/p>\n<p>Die festgelegte Sortierung sorgt daf&uuml;r, dass der aktuelle Datensatz ganz oben angezeigt wird, die &uuml;brigen folgen dann in der Reihenfolge der &auml;nderung weiter unten.<\/p>\n<p><b>Unterschiede anzeigen<\/b><\/p>\n<p>Nun w&auml;re es hilfreich, die Unterschiede zwischen den Datens&auml;tzen anzuzeigen. Wie aber sollen wir hier vorgehen Man k&ouml;nnte alle Unterschiede der archivierten Datens&auml;tze zum aktuellen Datensatz anzeigen. Alternativ zeigt man alle Unterschiede an, die sich von Version zu Version ergeben haben. Da dies ein sch&ouml;nes Beispiel f&uuml;r den Einsatz der bedingten Formatierung ist, wenden wir einfach beide Versionen an &#8211; der Benutzer kann dann zwischen den Versionen umschalten.<\/p>\n<p>Die erste Variante erhalten Sie, wenn Sie f&uuml;r die Felder des Datenblatts im Unterformular jeweils eine bedingte Formatierung festlegen, wie sie in Bild 14 dargestellt ist.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_014.png\" alt=\"Einstellen der bedingten Formatierung zur Anzeige der Unterschiede zur aktuellen Version\" width=\"700\" height=\"647,0345\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 14: Einstellen der bedingten Formatierung zur Anzeige der Unterschiede zur aktuellen Version<\/span><\/b><\/p>\n<p>Dazu klicken Sie erst auf das Feld, f&uuml;r das Sie die bedingte Formatierung festlegen wollen, und w&auml;hlen dann aus dem Ribbon den Eintrag <b>Daten-blatt|For-matierung|Bedingte For-matierung <\/b>aus. Klicken Sie im nun erscheinenden Dialog <b>Manager f&uuml;r Regeln zur bedingten Formatierung <\/b>auf die Schaltfl&auml;che <b>Neue Regel<\/b>. Im n&auml;chsten Dialog w&auml;hlen Sie im linken Kombinationsfeld <b>Feldwert <\/b>und im rechten Kombinationsfeld <b>Ungleich <\/b>aus. Geben Sie dann als Vergleichswert den folgenden Ausdruck ein:<\/p>\n<pre>DomWert(\"Vorname\";\"tblKunden\";\"KundeID = \" & [KundeID])<\/pre>\n<p>Dies vergleicht den Wert des Feldes <b>Vorname <\/b>f&uuml;r den aktuellen Datensatz mit dem Wert des Feldes der Tabelle <b>tblKunden<\/b> mit dem gleichen Wert im Feld <b>KundeID<\/b>. Dadurch, dass der oberste Datensatz der aktuelle Datensatz ist, stimmt dessen <b>Vorname <\/b>nat&uuml;rlich mit dem Vornamen des gleichen Datensatzes, diesmal mit der <b>DomWert<\/b>-Funktion referenziert, &uuml;berein &#8211; dadurch werden f&uuml;r diesen Datensatz niemals Unterschiede angezeigt.<\/p>\n<p>Was aber tun wir, wenn das Formular gerade die archivierten Datens&auml;tze f&uuml;r einen bereits gel&ouml;schten Datensatz anzeigt &#8211; wenn also kein Originaldatensatz mehr verf&uuml;gbar ist In diesem Fall sollten wir einfach die Unterschiede zwischen den einzelnen Versionen aufzeigen.<\/p>\n<p><b>Unterschiede von Version zu Version<\/b><\/p>\n<p>Dies wird etwas aufwendiger. Zun&auml;chst einmal wollen wir der Abfrage <b>qryKundenArchivUnion <\/b>ein Feld hinzuf&uuml;gen, dass den Wert des Feldes <b>ArchivKun-deID <\/b>der vorherigen Version des Datensatzes enth&auml;lt. Dies soll dann beispielsweise wie in Bild 15 aussehen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_015.png\" alt=\"Erweiterung der Abfrage qryKundenArchivUNION um ein Feld, das die ArchivKundeID der vorherigen Version enth&auml;lt\" width=\"700\" height=\"182,6411\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 15: Erweiterung der Abfrage qryKundenArchivUNION um ein Feld, das die ArchivKundeID der vorherigen Version enth&auml;lt<\/span><\/b><\/p>\n<p>Den Code der SQL-Abfrage &auml;ndern wir dazu wie folgt (neue Felder fett gedruckt):<\/p>\n<pre>SELECT 0 AS ArchivKundeID, \r\n''Aktuelle Version'' AS Aktion, \r\n... , \r\n\"\" AS VorherigeVersionID \r\nFROM tblKunden \r\nWHERE KundeID = [prmKundeID]\r\nUNION \r\nSELECT ArchivKundeID, Aktion, ... , \r\nNz(DMin(\"ArchivKundeID\",\r\n\"tblKundenArchiv\",\r\n\"KundeID= \" & KundeID \r\n& \" AND ArchivKundeID &gt; \" \r\n& ArchivKundeID),0) \r\nAS VorherigeVersionID\r\nFROM qryKundenArchiv \r\nWHERE KundeID = [prmKundeID]\r\nORDER BY Archivdatum DESC;<\/pre>\n<p>Im ersten Teil der <b>UNION<\/b>-Abfrage f&uuml;gen wir einfach nur das Feld <b>VorherigeVersionID <\/b>hinzu, das jedoch leer bleiben soll. Im zweiten Teil kommt ebenfalls das Feld <b>VorherigeVersionID <\/b>hinzu, diesmal allerdings mit einer Dom&auml;nenfunktion als Inhalt.<\/p>\n<p>Diese liefert den kleinsten Wert des Feldes <b>ArchivKundeID <\/b>der Abfrage <b>qryKundenArchiv<\/b>, wobei nur die Datens&auml;tze ber&uuml;cksichtigt werden, die als Versionen des mit dem Feld <b>KundeID <\/b>festgelegten Originaldatensatzes der Tabelle <b>tblKunden <\/b>angelegt wurden.<\/p>\n<p>Au&szlig;erdem soll das Feld <b>ArchivKundeID <\/b>des gesuchten Datensatzes gr&ouml;&szlig;er sein als der Wert des entsprechenden Feldes des aktuellen Datensatzes.<\/p>\n<p>Damit h&auml;tten wir also festgelegt, welcher Datensatz die Vorg&auml;ngerversion des aktuellen Datensatzes repr&auml;sentiert.<\/p>\n<p>Damit k&ouml;nnen wir nun zum Datenblatt des Unterformulars <b>sfmArchivierteDatensaetze <\/b>zur&uuml;ckkehren. Dort f&uuml;gen wir nun f&uuml;r alle Inhaltsfelder, also solche Felder, die aus der Originaltabelle <b>tblKunden <\/b>stammen, eine entsprechende bedingte Formatierung hinzu. Das Ergebnis soll wie in Bild 16 aussehen. Dort ist immer das Feld, das sich vom Feld der nachfolgenden Version unterscheidet, farblich hinterlegt.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_016.png\" alt=\"Markierung der Felder der Datens&auml;tze, die sich von den Feldern der vorherigen Version unterscheiden\" width=\"700\" height=\"345,0592\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 16: Markierung der Felder der Datens&auml;tze, die sich von den Feldern der vorherigen Version unterscheiden<\/span><\/b><\/p>\n<p>Wenn also der Vorname von Version 8 auf Version 9 von <b>Andr&eacute; <\/b>in <b>Andreas <\/b>ge&auml;ndert wurde, dann ist der Wert <b>Andr&eacute; <\/b>in Version 8 farbig markiert. Wenn sich der Nachname von Version 9 zu Version 10 von <b>Minhorst <\/b>auf <b>M&uuml;ller <\/b>&auml;ndert, dann soll der Eintrag <b>Minhorst <\/b>in Version 9 farbig hinterlegt werden.<\/p>\n<p>Nun ben&ouml;tigen wir nur noch die entsprechende Bedingung f&uuml;r die bedingte Formatierung der betroffenen Felder beziehungsweise Steuerelemente. Diese legen Sie etwa f&uuml;r das Feld <b>Vorname <\/b>wie in Bild 17 an.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_017.png\" alt=\"Hinzuf&uuml;gen einer bedingten Formatierung zum Vergleich der einzelnen Versionen\" width=\"700\" height=\"388,8889\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 17: Hinzuf&uuml;gen einer bedingten Formatierung zum Vergleich der einzelnen Versionen<\/span><\/b><\/p>\n<p>Der Feldwert des Feldes mit der bedingten Formatierung soll ungleich dem folgenden Ausdruck sein:<\/p>\n<pre>Nz(DomWert(\"Vorname\";\"qryKundenArchiv\";\r\n\"KundeID = \" & [KundeID] \r\n& \" AND ArchivKundeID = \" \r\n& [VorherigeVersionID]);\r\nDomWert(\"Vorname\";\"tblKunden\";\r\n\"KundeID = \" & [KundeID]))<\/pre>\n<p>Nur in diesem Fall soll die Formatierung angewendet werden, hier ein farbiger Hintergrund.<\/p>\n<p>Der Ausdruck fragt den Wert des Feldes <b>Vorname <\/b>f&uuml;r den Datensatz ab, dessen Feld <b>ArchivKundeID <\/b>den Wert enth&auml;lt, den das Feld <b>VorherigeVersionID <\/b>des zu formatierenden Datensatzes enth&auml;lt.<\/p>\n<p>Im Beispiel des Datensatzes mit dem Wert <b>8 <\/b>im Feld <b>ArchivKundeID <\/b>aus der Abbildung also etwa den Datensatz, dessen Feld <b>ArchivKundeID <\/b>den Wert <b>9 <\/b>enth&auml;lt. Dieses Feld enth&auml;lt im Feld <b>Vorname <\/b>den Wert <b>Andreas<\/b>, w&auml;hrend das zu formatierende Feld den Wert <b>Andr&eacute; <\/b>enth&auml;lt. Die Werte sind ungleich, also wendet Access die vorgegebene bedingte Formatierung an.<\/p>\n<p>Beim Feld <b>Firma <\/b>sieht die Formel f&uuml;r den Vergleichswert in der bedingten Formatierung f&uuml;r dieses Feld so aus:<\/p>\n<pre>Nz(DomWert(\"Firma\";\"qryKundenArchiv\";\r\n\"KundeID = \" & [KundeID] \r\n& \" AND ArchivKundeID = \" \r\n& [VorherigeVersionID]);\r\nDomWert(\"Firma\";\r\n\"tblKunden\";\r\n\"KundeID = \" & [KundeID]))<\/pre>\n<p>Hier soll im Beispiel aus Bild 18 der Wert des ersten archivierten Datensatzes, also <b>Andr&eacute; Minhorst Verlag<\/b>, mit dem Wert des aktuellen Datensatzes verglichen werden, also mit <b>Andreas M&uuml;ller Verlag<\/b>. Die innerhalb des obigen Ausdrucks verwendete <b>DomWert<\/b>-Funktion liefert keinen passenden Datensatz, wodurch der zweite Teil der umschlie&szlig;enden <b>Nz<\/b>-Funktion ausgewertet wird. Dieser ermittelt den Wert des beobachteten Feldes f&uuml;r den aktuellen Datensatz aus der Tabelle <b>tblKunden<\/b>. Die beiden Versionen sind verschieden, daher wird das in der Abbildung markierte Feld entsprechend formatiert.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_018.png\" alt=\"Markierung der Felder der Datens&auml;tze, die sich von den Feldern der vorherigen Version unterscheiden - andere Version\" width=\"700\" height=\"244,4544\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 18: Markierung der Felder der Datens&auml;tze, die sich von den Feldern der vorherigen Version unterscheiden &#8211; andere Version<\/span><\/b><\/p>\n<p><b>Flei&szlig;arbeit<\/b><\/p>\n<p>Nachdem wir die passende Bedingung f&uuml;r die bedingte Formatierung entwickelt haben, m&uuml;ssen wir diese f&uuml;r alle betroffenen Felder einstellen. Das ist dummerweise reine Flei&szlig;arbeit, die &uuml;berdies noch fehleranf&auml;llig ist. Zum Gl&uuml;ck kann man die bedingte Formatierung auch per VBA zuweisen, was wir im Folgenden so erledigen. Dazu rufen wir in der Ereignisprozedur <b>Form_Load<\/b> eine weitere Prozedur namens <b>BedingteFormatierung <\/b>auf:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     ...\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> IsNull(Me.OpenArgs)<span style=\"color:blue;\"> Then<\/span>\r\n         ...\r\n         BedingteFormatierung\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Diese Prozedur sieht wie in Listing 3 aus und speichert zun&auml;chst einen Verweis auf die aktuelle Datenbank in der Variablen <b>db<\/b>. Dann referenziert sie die Tabellendefinition der Tabelle <b>tblKunden <\/b>mit der <b>TableDef<\/b>-Variablen <b>tdf<\/b>.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>BedingteFormatierung()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>tdf<span style=\"color:blue;\"> As <\/span>DAO.TableDef\r\n     <span style=\"color:blue;\">Dim <\/span>fld<span style=\"color:blue;\"> As <\/span>DAO.Field\r\n     <span style=\"color:blue;\">Dim <\/span>ctl<span style=\"color:blue;\"> As <\/span>Control\r\n     <span style=\"color:blue;\">Dim <\/span>strKriterium<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>objFormatCondition<span style=\"color:blue;\"> As <\/span>FormatCondition\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> tdf = db.TableDefs(\"tblKunden\")\r\n     For Each ctl In Me!sfmArchivierteDatensaetze.Form.Controls\r\n         On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> fld = Nothing\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(ctl.ControlSource) &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n             <span style=\"color:blue;\">Set<\/span> fld = tdf.Fields(ctl.ControlSource)\r\n         <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> fld Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n             <span style=\"color:blue;\">Do While<\/span> ctl.FormatConditions.Count &gt; 0\r\n                 ctl.FormatConditions(0).Delete\r\n             <span style=\"color:blue;\">Loop<\/span>\r\n             strKriterium = \"Nz(DLookUp(\"\"\" & ctl.ControlSource & \"\"\",\"\"qryKundenArchiv\"\",\"\"KundeID = \"\" & [KundeID] _\r\n                 & \"\" AND ArchivKundeID = \"\" & [VorherigeVersionID]),DLookUp(\"\"\" & ctl.ControlSource _\r\n                 & \"\"\",\"\"tblKunden\"\",\"\"KundeID = \"\" & [KundeID]))\"\r\n             <span style=\"color:blue;\">Set<\/span> objFormatCondition = ctl.FormatConditions.Add(acFieldValue, acNotEqual, strKriterium)\r\n             objFormatCondition.BackColor = &HFF\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Next<\/span> ctl\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Definieren der bedingten Formatierung f&uuml;r die Steuerelemente im Unterformular<\/span><\/b><\/p>\n<p>Dann durchl&auml;uft die Prozedur alle Steuerelemente des Unterformulars <b>sfmArchivierteDatensaetze<\/b> in einer <b>For Each<\/b>-Schleife &uuml;ber die Elemente der <b>Controls<\/b>-Auflistung. Das aktuelle Steuerelement referenziert die Prozedur dabei mit der Variablen <b>ctl<\/b>. W&auml;hrend die Prozedur die Fehlerbehandlung deaktiviert, leert sie eine Variable namens <b>fld <\/b>und versucht diese gleich danach wieder zu f&uuml;llen &#8211; und zwar mit einem Verweis auf ein Feld der Tabelle <b>tblKunden<\/b>, dessen Feldname mit dem Feld &uuml;bereinstimmt, an welches das aktuelle mit <b>ctl <\/b>referenzierte Steuerelement gebunden ist &#8211; dies allerdings nur, wenn das Steuerelement &uuml;berhaupt an ein Feld gebunden ist. Dies wiederum pr&uuml;ft die Prozedur &uuml;ber den Ausdruck <b>Len(ctl.ControlSource) > 0<\/b> &#8211; sprich: Enth&auml;lt die Eigenschaft <b>Controls <\/b>des Steuerelements <b>ctl <\/b>&uuml;berhaupt einen Wert, dessen L&auml;nge gr&ouml;&szlig;er als <b>0 <\/b>ist<\/p>\n<p>Falls ja, kann es sich ja auch um ein Feld handeln, das nicht aus der Tabelle <b>tblKunden <\/b>stammt, sondern um ein anderes Feld der Datenherkunft <b>qryKundenArchivUNION<\/b>.<\/p>\n<p>Sowohl das Pr&uuml;fen der L&auml;nge der Eigenschaft <b>ControlSource <\/b>f&uuml;r ein Steuerelement, das diese Eigenschaft gar nicht aufweist, als auch das Referenzieren eines Feldes einer Tabellendefinition, das nicht vorhanden ist, kann einen Fehler ausl&ouml;sen &#8211; deshalb haben wir die Fehlerbehanldung mit <b>On Error Resume Next <\/b>tempor&auml;r ausgeschaltet und stellen diese nach dem Versuch, <b>fld <\/b>mit dem Verweis auf das Feld zu f&uuml;llen, an welches das aktuelle Steuerelement gebunden ist, mit <b>On Error Goto 0 <\/b>wieder ein.<\/p>\n<p>Sollte <b>fld <\/b>hiernach nicht <b>Nothing <\/b>sein, referenziert <b>ctl <\/b>ein Steuerelement, das an ein Feld der Tabelle <b>tblKunden <\/b>gebunden ist. In diesem Fall (<b>Not fld Is Nothing<\/b>) durchl&auml;uft die Prozedur so lange eine <b>Do While<\/b>-Schleife, bis die Anzahl der bedingten Formatierungen f&uuml;r dieses Steuerelement gleich <b>0 <\/b>ist. Damit dies irgendwann geschieht, l&ouml;scht die einzige Anweisung innerhalb der <b>Do While<\/b>-Schleife das jeweils erste Element der Auflistung mit der <b>Delete<\/b>-Methode dieses Elements.<\/p>\n<p>Dann stellt sie ein Kriterium zusammen, das dem oben beschriebenen Vergleichsausdruck f&uuml;r das jeweilige Feld entspricht. Damit wir dieses f&uuml;r jedes einzelne Feld dynamisch generieren k&ouml;nnen, enth&auml;lt der Ausdruck mit <b>ctl.ControlSource <\/b>den Namen des Feldes, an welches das Steuerelement gebunden ist.<\/p>\n<p>Die folgende Anweisung definiert die bedingte Formatierung und referenziert diese mit der Variablen <b>objFormatCondition<\/b>. Beim Anlegen verwendet diese Anweisung als ersten Parameter den Wert <b>acFieldValue<\/b>, was der Einstellung <b>Feldwert ist <\/b>im Dialog <b>Formatierungsregel bearbeiten <\/b>entspricht. Den zweiten Parameter stellt die Prozedur auf <b>acNotEqual <\/b>ein (entspricht <b>Ungleich<\/b>). Schlie&szlig;lich folgt als dritter Parameter der zuvor zusammengestellte und in der Variablen <b>strKriterium <\/b>gespeicherte Vergleichsausdruck. Die Referenzierung der bedingten Formatierung war n&ouml;tig, weil wir die Formatierung nicht direkt beim Anlegen &uuml;bergeben k&ouml;nnen, sondern dies mit einer Eigenschaft des Objekts <b>objFormatCondition <\/b>erledigen m&uuml;ssen. Diese Eigenschaft f&uuml;r die Einstellung der Hintergrundfarbe lautet BackColor und erh&auml;lt den gew&uuml;nschten Farbwert beispielsweise als Hexadezimalzahl.<\/p>\n<p>Auf diese Weise durchl&auml;uft die Prozedur alle Steuerelemente und legt f&uuml;r all jene, die an ein Feld der Tabelle <b>tblKunden <\/b>gebunden sind, die entsprechende bedingte Formatierung fest.<\/p>\n<p><b>Vorherige Version wiederherstellen<\/b><\/p>\n<p>Da wir nun die optische Seite des Formulars <b>frmArchivierteDatensaetze<\/b> und seines Unterformulars hergestellt haben, k&uuml;mmern wir uns um die eigentlich interessante Funktion: das Wiederherstellen vorheriger Versionen eines Datensatzes.<\/p>\n<p>An dieser Stelle m&uuml;ssen wir uns einig werden, was geschehen soll, wenn der Benutzer eine &auml;ltere Version eines Datensatzes der Tabelle <b>tblKunden <\/b>anklickt und dann die Schaltfl&auml;che <b>cmdWiederherstellen <\/b>bet&auml;tigt. Eins ist klar: Der zu diesem Zeitpunkt aktivierte Datensatz im Unterformular <b>sfmArchivierteDatensaetze <\/b>soll als aktueller Datensatz mit dem aktuellen Wert des Feldes <b>KundeID <\/b>in die Tabelle <b>tblKunden <\/b>&uuml;bernommen werden.<\/p>\n<p>Die erste Frage, die sich hier stellt, ist: Wie geschieht dies Soll der aktuelle Datensatz zun&auml;chst gel&ouml;scht und dann der wiederherzustellende Datensatz als neuer Datensatz in der Tabelle <b>tblKunden <\/b>angelegt werden<\/p>\n<p>Oder &uuml;berschreiben wir den aktuellen Datensatz mit dem entsprechenden Wert im Feld <b>KundeID <\/b>mit dem wiederherzustellenden Datensatz Die zweite Frage ist: Was geschieht mit den Daten in der Tabelle <b>tblKundenArchiv<\/b> Werden alle Versionen, die sich zwischen der aktuellen Version und der wiederherzustellenden Version befinden, gel&ouml;scht Oder wollen wir die Historie komplett beibehalten und f&uuml;gen der Tabelle <b>tblKundenArchiv <\/b>den aktuellen Datensatz der Tabelle <b>tblKunden <\/b>vor dem Wiederherstellen des entsprechenden Datensatzes wie gewohnt als neue fr&uuml;here Version hinzu<\/p>\n<p>Wir w&auml;hlen in beiden F&auml;llen die zuletzt vorgestellte Variante, was auch prima zusammenpasst. Wenn wir in einer SQL-UPDATE-Anweisung die Werte des wiederherzustellenden Datensatzes in die Felder des aktuellen Datensatzes mit dem gleichen Wert im Feld <b>KundeID <\/b>eintragen, l&ouml;st dies ja wieder automatisch das Tabellenereignis <b>Nach Aktualisierung <\/b>aus, wodurch die aktuelle Version des Datensatzes vor dem &uuml;berschreiben mit dem wiederherzustellenden Datensatz als neuer Datensatz in die Tabelle <b>tblKundenArchiv <\/b>geschrieben wird.<\/p>\n<p>Die notwendige Prozedur sieht wie in Listing 4 aus. Sie ruft mit der <b>Execute<\/b>-Methode des aktuellen <b>Database<\/b>-Objekts eine <b>UPDATE<\/b>-Anweisung auf, die den Datensatz in der Tabelle <b>tblKunden<\/b>, dessen Feld <b>KundeID <\/b>den Wert des gleichen Feldes im aktuell markierten Datensatz des Unterformulars <b>sfmArchivierteDatensaetze <\/b>enth&auml;lt, mit den Feldinhalten des wiederherzustellenden Datensatzes f&uuml;llt.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdWiederherstellen_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">With<\/span> Me!sfmArchivierteDatensaetze.Form\r\n         db.Execute \"UPDATE tblKunden SET AnredeID = \" & !AnredeID & \", Vorname = ''\" _\r\n             & !Vorname & \"'', Nachname = ''\" _\r\n             & !Nachname & \"'', Firma = ''\" & !Firma & \"'', Strasse = ''\" & !Strasse & \"'', PLZ = ''\" _\r\n             & !PLZ & \"'', Ort = ''\" & !Ort _\r\n             & \"'', Land = ''\" & !Land & \"'' WHERE KundeID = \" & !KundeID, dbFailOnError\r\n         Me!sfmArchivierteDatensaetze.Form.Requery\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Wiederherstellen einer &auml;lteren Version eines Datensatzes<\/span><\/b><\/p>\n<p>Das Ergebnis zeigt Bild 19. Hier wurde der unterste Datensatz wiederhergestellt, der nun wieder als erster Datensatz im Unterformular <b>sfmArchivierteDatensaetze <\/b>auftaucht.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_019.png\" alt=\"Archivierte Datens&auml;tze mit einem wiederhergestellten Datensatz\" width=\"700\" height=\"271,4521\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 19: Archivierte Datens&auml;tze mit einem wiederhergestellten Datensatz<\/span><\/b><\/p>\n<p><b>Gel&ouml;schte Datens&auml;tze wiederherstellen<\/b><\/p>\n<p>Beim Wiederherstellen gel&ouml;schter Datens&auml;tze gibt es zun&auml;chst ein ganz anderes Problem: Wir ben&ouml;tigen erst einmal eine &uuml;bersicht aller bereits gel&ouml;schten Datens&auml;tze. Legen wir also zun&auml;chst ein oder zwei neue Datens&auml;tze in der Tabelle <b>tblKunden <\/b>an, die wir direkt wieder l&ouml;schen oder erst nach dem Ausf&uuml;hren einiger &auml;nderungen. Die gel&ouml;schten Datens&auml;tze werden dann wie in Bild 20 in der Archivtabelle angezeigt.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_020.png\" alt=\"Archivtabelle mit zwei gel&ouml;schten Datens&auml;tzen\" width=\"700\" height=\"186,7978\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 20: Archivtabelle mit zwei gel&ouml;schten Datens&auml;tzen<\/span><\/b><\/p>\n<p>Passend dazu kopieren wir nun das Formular <b>frmArchivierteDatensaetze<\/b> in das Formular <b>frmGeloeschteDatensaetze <\/b>(s. Bild 21). Die Ereignisprozedur, die durch das Ereignis <b>Beim Laden <\/b>ausgel&ouml;st wird, k&ouml;nnen Sie l&ouml;schen. Das Unterformular <b>sfmArchivierteDatensaetze <\/b>kopieren wir in das Unterformular <b>sfmGeloeschteDatensaetze<\/b>.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_021.png\" alt=\"Formular zur Anzeige der gel&ouml;schten Datens&auml;tze\" width=\"575\" height=\"373,4971\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 21: Formular zur Anzeige der gel&ouml;schten Datens&auml;tze<\/span><\/b><\/p>\n<p>Das Unterformular-Steuerelement <b>sfmArchivierteDatensaetze <\/b>statten wir dementsprechend mit dem Namen <b>sfmGeloeschteDatensaetze <\/b>aus und weisen diesem das entsprechende Unterformular als <b>Herkunftsobjekt <\/b>zu. Die Datenherkunft passen wir wie in Bild 22 an, indem wir die Abfrage <b>qryKundenArchiv<\/b> in die neue Abfrage <b>qryKundenGeloescht <\/b>kopieren und dieser ein Kriterium f&uuml;r das Feld <b>Aktion <\/b>zuweisen. Der Vergleichswert lautet <b>&#8222;Geloescht&#8220;<\/b>, wodurch die Abfrage nur noch als gel&ouml;scht markierte Datens&auml;tze liefert.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_022.png\" alt=\"Datenherkunft des Unterformulars sfmDatensaetzeGeloescht\" width=\"575\" height=\"239,0283\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 22: Datenherkunft des Unterformulars sfmDatensaetzeGeloescht<\/span><\/b><\/p>\n<p>Schlie&szlig;lich weist das Formular <b>frmGeloeschteDatensaetze <\/b>zwei Schaltfl&auml;chen auf. Die erste hei&szlig;t <b>cmdHistorieAnzeigen <\/b>und l&ouml;st eine &auml;hnliche Prozedur aus wie die gleichnamige Schaltfl&auml;che des Formulars <b>frmKunden <\/b>(s. Listing 5). Diese &ouml;ffnet das Formular <b>frmArchivierteDatensaetze <\/b>und filtert es nach dem Wert des Feldes <b>KundeID <\/b>f&uuml;r den betroffenen Datensatz.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdHistorieAnzeigen_Click()\r\n     DoCmd.OpenForm \"frmArchivierteDatensaetze\", OpenArgs:=Me!sfmGeloeschteDatensaetze.Form!KundeID\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 5: Anzeigen eines gel&ouml;schten Datensatzes in der Archivansicht<\/span><\/b><\/p>\n<p>Die zweite hei&szlig;t <b>cmdWiederherstellen<\/b> und l&ouml;st die Prozedur aus Listing 6 aus. Die Prozedur f&uuml;hrt eine <b>INSERT INTO<\/b>-Anweisung aus, die alle Feldinhalte aus dem aktuell markierten Datensatz des Unterformulars <b>sfmGeloeschteDatensaetze <\/b>in die Tabelle <b>tblKunden <\/b>schreibt. Sie pr&uuml;ft, ob der Datensatz erfolgreich in die Ursprungstabelle eingetragen wurde und l&ouml;scht in diesem Fall den entsprechenden Datensatz aus der Tabelle <b>tblKundenArchiv<\/b>. Dies kann beispielsweise in dem Fall zu Problemen f&uuml;hren, wenn der Benutzer etwa den Datensatz mit dem Wert <b>7 <\/b>im Feld <b>KundeID <\/b>l&ouml;scht (wodurch dieser unter diesem Prim&auml;rschl&uuml;sselwert in der Tabelle <b>tblKundenArchiv <\/b>gespeichert wird), dann die Datenbank komprimiert und der Datensatz mit der Nummer <b>7 <\/b>zuf&auml;llig der neueste Datensatz der Tabelle war. In diesem Fall wird ein neuer Datensatz in der Tabelle <b>tblKunden <\/b>wiederum mit dem Wert <b>7 <\/b>im Feld <b>KundeID <\/b>angelegt. F&uuml;r diesen Fall m&uuml;ssten Sie der Prozedur noch eine entsprechende Variante hinzuf&uuml;gen, welche den wiederherzustellenden Datensatz unter einem anderen Prim&auml;rschl&uuml;sselwert wiederherstellt.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdWiederherstellen_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>dao.Database\r\n     <span style=\"color:blue;\">Dim <\/span>lngIDNeu<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">With<\/span> Me!sfmGeloeschteDatensaetze.Form\r\n         On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n         db.Execute \"INSERT INTO tblKunden SELECT KundeID, AnredeID, Vorname, &quot; _\r\n             &quot;Nachname, Firma, Strasse, PLZ, Ort, Land \" _\r\n             & \"FROM tblKundenArchiv WHERE KundeID = \" & !KundeID _\r\n             & \" AND NOT GeloeschtAm IS NULL\", dbFailOnError\r\n         <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n         <span style=\"color:blue;\">If <\/span>db.RecordsAffected = 1<span style=\"color:blue;\"> Then<\/span>\r\n             lngIDNeu = db.OpenRecordset(\"SELECT @@IDENTITY\").Fields(0)\r\n             <span style=\"color:blue;\">If <\/span>lngIDNeu = !KundeID<span style=\"color:blue;\"> Then<\/span>\r\n                 db.Execute \"DELETE FROM tblKundenArchiv WHERE KundeID = \" & lngIDNeu, dbFailOnError\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> \"Der Datensatz konnte nicht wiederherstellt werden.\"\r\n         <span style=\"color:blue;\">End If<\/span>\r\n         Me!sfmGeloeschteDatensaetze.Form.Requery\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = Nothing\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 6: Direktes Wiederherstellen eines gel&ouml;schten Datensatzes<\/span><\/b><\/p>\n<p><b>Archivierung deaktivieren<\/b><\/p>\n<p>F&uuml;r verschiedene Zwecke kann es sinnvoll sein, die Archivierung zu deaktivieren. Dazu m&uuml;ssen Sie in allen beteiligten Makros eine entsprechende Abfrage eines Wertes etwa aus einer Optionentabelle integrieren, die beispielsweise <b>tblOptionen <\/b>hei&szlig;t und im Entwurf wie in Bild 23 aussieht. Wenn diese den Wert <b>-1<\/b> enth&auml;lt, soll keine Archivierung stattfinden. Damit der Wert des Feldes <b>ArchivierungDeaktiviert <\/b>dieser Tabelle beim &auml;ndern oder L&ouml;schen eines Datensatzes der Tabelle <b>tblKunden <\/b>abgefragt wird, m&uuml;ssen Sie den beiden Tabellenereignismakros, die durch die Ereignisse <b>Nach Aktualisierung <\/b>und <b>Nach L&ouml;schung<\/b> ausgel&ouml;st werden, ein paar Makroaktionen voranstellen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_023.png\" alt=\"Tabelle zum Speichern der Option zum Deaktivieren der Archivierung\" width=\"450\" height=\"293,0233\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 23: Tabelle zum Speichern der Option zum Deaktivieren der Archivierung<\/span><\/b><\/p>\n<p>Diese sehen etwa wie in Bild 24 aus. Der erste Befehl ruft den einzigen Datensatz der Tabelle <b>tblOptionen <\/b>ab. Die zweite vergleicht den Wert des Feldes <b>ArchivierungDeaktiviert <\/b>dieses Feldes mit dem Wert <b>-1<\/b>. Ist diese Bedingung wahr, tr&auml;gt das Makro einen entsprechenden Hinweis in die Protokolltabelle <b>USysApplicationLog <\/b>ein und beendet das Makro. Falls die Bedingung nicht wahr ist, archiviert das Makro den ge&auml;nderten Datensatz wie gewohnt. Die gleichen Makroaktionen m&uuml;ssen Sie auch den &uuml;brigen Makros hinzuf&uuml;gen, welche gegebenenfalls deaktiviert werden sollen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2014_02\/pic_925_024.png\" alt=\"Erweiterung des Datenmakros um eine Pr&uuml;fung der Option ArchivierungDeaktiviert aus der Tabelle tblOptionen\" width=\"450\" height=\"415,2632\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 24: Erweiterung des Datenmakros um eine Pr&uuml;fung der Option ArchivierungDeaktiviert aus der Tabelle tblOptionen<\/span><\/b><\/p>\n<p><b>Optimierung beim &ouml;ffnen der Formulare<\/b><\/p>\n<p>Wenn Sie vom Formular <b>frmKunden <\/b>das Formular <b>frmArchivierteDatensaetze <\/b>oder <b>frmGeloeschteDatensaetze <\/b>&ouml;ffnen oder vom Formular <b>frmGeloeschteDatensaetze <\/b>das Formular <b>frmArchivierteDatensaetze<\/b>, erscheinen diese direkt &uuml;ber dem zuvor ge&ouml;ffneten Formular. Damit dies nicht geschieht, f&uuml;gen wir den entsprechenden Ereignisprozeduren noch einige Zeilen hinzu, die das neu ge&ouml;ffnete Formular jeweils etwas nach rechts unten verschieben &#8211; so zum Beispiel beim &ouml;ffnen des Formulars <b>frmArchivierteDatensaetze<\/b>.<\/p>\n<p>Dabei speichern wir zun&auml;chst die Position des aktuellen Formulars in den Variablen <b>lngLeft <\/b>und <b>lngTop <\/b>und verschieben das neu ge&ouml;ffnete Formular dann von dieser Position aus um 1.000 Twips nach rechts und nach unten:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdHistorieAnzeigen_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>lngLeft<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngTop<span style=\"color:blue;\"> As Long<\/span>\r\n     lng<span style=\"color:blue;\">Left<\/span> = Me.WindowLeft\r\n     lngTop = Me.WindowTop\r\n     DoCmd.OpenForm ...\r\n     DoCmd.MoveSize lng<span style=\"color:blue;\">Left<\/span> + 1000, _\r\n         lngTop + 1000\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b>Zusammenfassung und Ausblick<\/b><\/p>\n<p>Mit diesem Beitrag haben Sie ein Praxisbeispiel f&uuml;r den Einsatz der neuen Tabellenereignisse von Access 2010 und neuer erhalten. Wenn Sie diese Technik einsetzen m&ouml;chten, um die &auml;nderungen in mehr als einer Tabelle einer Datenbank zu archivieren, sollten Sie die entsprechenden Archivtabellen und Tabellenereignisse gegebenenfalls per VBA erstellen lassen.<\/p>\n<p>Wie dies gelingen kann, schauen wir uns gegebenenfalls in einem sp&auml;teren Beitrag an.<\/p>\n<h3>Downloads zu diesem Beitrag<\/h3>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>GeaenderteDatenArchivieren.accdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/{E9025507-D8A1-4C8C-A0AE-38BBBBAC2D38}\/aiu_925.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Das &Auml;ndern von Daten ist in manchen F&auml;llen an der Tagesordnung &#8211; zum Beispiel bei Kundendaten. Wenn Sie in einer Datenbank Kunden verwalten, die nicht nur f&uuml;r einen einzigen Auftrag oder eine einzige Bestellung angelegt werden, sondern mit denen Sie wiederkehrend gesch&auml;ftlichen Kontakt haben, m&uuml;ssen Sie die Adressdaten auf dem aktuellen Stand halten. Kein Problem: Die entsprechenden Felder lassen sich ja schnell mal &uuml;berschreiben. Das Problem ist nur, dass hier auch Fehler geschehen. Wenn Sie beispielsweise beim falschen Michael M&uuml;ller die Lieferadresse &auml;ndern, freut sich ein Michael M&uuml;ller &uuml;ber eine Lieferung, aber der andere, der nichts bestellt hat, wird die Rechnung wohl nicht bezahlen. Und wenn sich solche Fehler nur schwer verhindern lassen, so sollen Sie zumindest die Adressdaten schnell wiederherstellen k&ouml;nnen &#8230;<\/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":[66022014,662014,44000021],"tags":[],"class_list":["post-55000925","post","type-post","status-publish","format-standard","hentry","category-66022014","category-662014","category-Tabellen_und_Datenmodellierung"],"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>Ge&auml;nderte Daten archivieren - 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\/Geaenderte_Daten_archivieren\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Ge&auml;nderte Daten archivieren\" \/>\n<meta property=\"og:description\" content=\"Das &Auml;ndern von Daten ist in manchen F&auml;llen an der Tagesordnung - zum Beispiel bei Kundendaten. Wenn Sie in einer Datenbank Kunden verwalten, die nicht nur f&uuml;r einen einzigen Auftrag oder eine einzige Bestellung angelegt werden, sondern mit denen Sie wiederkehrend gesch&auml;ftlichen Kontakt haben, m&uuml;ssen Sie die Adressdaten auf dem aktuellen Stand halten. Kein Problem: Die entsprechenden Felder lassen sich ja schnell mal &uuml;berschreiben. Das Problem ist nur, dass hier auch Fehler geschehen. Wenn Sie beispielsweise beim falschen Michael M&uuml;ller die Lieferadresse &auml;ndern, freut sich ein Michael M&uuml;ller &uuml;ber eine Lieferung, aber der andere, der nichts bestellt hat, wird die Rechnung wohl nicht bezahlen. Und wenn sich solche Fehler nur schwer verhindern lassen, so sollen Sie zumindest die Adressdaten schnell wiederherstellen k&ouml;nnen ...\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Geaenderte_Daten_archivieren\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2020-05-22T21:19:28+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg07.met.vgwort.de\/na\/9eacc3746a4a475da7befb79424b9aeb\" \/>\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=\"30\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Geaenderte_Daten_archivieren\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Geaenderte_Daten_archivieren\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Ge&auml;nderte Daten archivieren\",\"datePublished\":\"2020-05-22T21:19:28+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Geaenderte_Daten_archivieren\\\/\"},\"wordCount\":5394,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Geaenderte_Daten_archivieren\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg07.met.vgwort.de\\\/na\\\/9eacc3746a4a475da7befb79424b9aeb\",\"articleSection\":[\"2\\\/2014\",\"2014\",\"Tabellen und Datenmodellierung\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Geaenderte_Daten_archivieren\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Geaenderte_Daten_archivieren\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Geaenderte_Daten_archivieren\\\/\",\"name\":\"Ge&auml;nderte Daten archivieren - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Geaenderte_Daten_archivieren\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Geaenderte_Daten_archivieren\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg07.met.vgwort.de\\\/na\\\/9eacc3746a4a475da7befb79424b9aeb\",\"datePublished\":\"2020-05-22T21:19:28+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Geaenderte_Daten_archivieren\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Geaenderte_Daten_archivieren\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Geaenderte_Daten_archivieren\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg07.met.vgwort.de\\\/na\\\/9eacc3746a4a475da7befb79424b9aeb\",\"contentUrl\":\"http:\\\/\\\/vg07.met.vgwort.de\\\/na\\\/9eacc3746a4a475da7befb79424b9aeb\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Geaenderte_Daten_archivieren\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Ge&auml;nderte Daten archivieren\"}]},{\"@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":"Ge&auml;nderte Daten archivieren - 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\/Geaenderte_Daten_archivieren\/","og_locale":"de_DE","og_type":"article","og_title":"Ge&auml;nderte Daten archivieren","og_description":"Das &Auml;ndern von Daten ist in manchen F&auml;llen an der Tagesordnung - zum Beispiel bei Kundendaten. Wenn Sie in einer Datenbank Kunden verwalten, die nicht nur f&uuml;r einen einzigen Auftrag oder eine einzige Bestellung angelegt werden, sondern mit denen Sie wiederkehrend gesch&auml;ftlichen Kontakt haben, m&uuml;ssen Sie die Adressdaten auf dem aktuellen Stand halten. Kein Problem: Die entsprechenden Felder lassen sich ja schnell mal &uuml;berschreiben. Das Problem ist nur, dass hier auch Fehler geschehen. Wenn Sie beispielsweise beim falschen Michael M&uuml;ller die Lieferadresse &auml;ndern, freut sich ein Michael M&uuml;ller &uuml;ber eine Lieferung, aber der andere, der nichts bestellt hat, wird die Rechnung wohl nicht bezahlen. Und wenn sich solche Fehler nur schwer verhindern lassen, so sollen Sie zumindest die Adressdaten schnell wiederherstellen k&ouml;nnen ...","og_url":"https:\/\/access-im-unternehmen.de\/Geaenderte_Daten_archivieren\/","og_site_name":"Access im Unternehmen","article_published_time":"2020-05-22T21:19:28+00:00","og_image":[{"url":"http:\/\/vg07.met.vgwort.de\/na\/9eacc3746a4a475da7befb79424b9aeb","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"30\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/Geaenderte_Daten_archivieren\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Geaenderte_Daten_archivieren\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Ge&auml;nderte Daten archivieren","datePublished":"2020-05-22T21:19:28+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Geaenderte_Daten_archivieren\/"},"wordCount":5394,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Geaenderte_Daten_archivieren\/#primaryimage"},"thumbnailUrl":"http:\/\/vg07.met.vgwort.de\/na\/9eacc3746a4a475da7befb79424b9aeb","articleSection":["2\/2014","2014","Tabellen und Datenmodellierung"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Geaenderte_Daten_archivieren\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Geaenderte_Daten_archivieren\/","url":"https:\/\/access-im-unternehmen.de\/Geaenderte_Daten_archivieren\/","name":"Ge&auml;nderte Daten archivieren - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Geaenderte_Daten_archivieren\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Geaenderte_Daten_archivieren\/#primaryimage"},"thumbnailUrl":"http:\/\/vg07.met.vgwort.de\/na\/9eacc3746a4a475da7befb79424b9aeb","datePublished":"2020-05-22T21:19:28+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Geaenderte_Daten_archivieren\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Geaenderte_Daten_archivieren\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/Geaenderte_Daten_archivieren\/#primaryimage","url":"http:\/\/vg07.met.vgwort.de\/na\/9eacc3746a4a475da7befb79424b9aeb","contentUrl":"http:\/\/vg07.met.vgwort.de\/na\/9eacc3746a4a475da7befb79424b9aeb"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Geaenderte_Daten_archivieren\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Ge&auml;nderte Daten archivieren"}]},{"@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\/55000925","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=55000925"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55000925\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55000925"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55000925"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55000925"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}