{"id":55001112,"date":"2017-12-01T00:00:00","date_gmt":"2022-03-30T19:04:49","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1112"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Daten_anonymisieren","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Daten_anonymisieren\/","title":{"rendered":"Daten anonymisieren"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg09.met.vgwort.de\/na\/be02bba40d084c0c91fe4c4398cee7a2\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>Wenn ein potenzieller Kunde Sie um Unterst&uuml;tzung beim Programmieren oder Anpassen einer bestehenden Datenbank bittet, ist es am einfachsten, wenn diese Ihnen die Datenbank zum Analysieren zur Verf&uuml;gung stellt. Das scheitert aber oft daran, dass der Kunde die Datenbank nicht herausgeben darf, weil die enthaltenen Daten nicht weitergegeben werden d&uuml;rfen. Oft handelt es sich dabei um Adressdaten. Dieser Beitrag zeigt, wie Sie dem Kunden das Werkzeug bereitstellen, die enthaltenen Daten zu anonymisieren.<\/b><\/p>\n<h2>Warum anonymisieren<\/h2>\n<p>Aber warum sollte man sich &uuml;berhaupt die M&uuml;he machen, die Daten zu anonymisieren Es w&auml;re doch viel einfacher, einfach die Datenbank zu kopieren und die enthaltenen Daten vor der Weitergabe zu l&ouml;schen. Klar, das geht viel schneller: Allerdings enth&auml;lt die Datenbank dann auch keine Daten und der Entwickler &#8211; in diesem Fall Sie &#8211; oder der Kunde m&uuml;ssen manuell einige Testdatens&auml;tze eingeben. Und manchmal sollen ja gerade Performance-Probleme behoben werden. In diesem Fall ist es nat&uuml;rlich komplett abwegig, eine leere Datenbank an den Entwickler zu &uuml;bergeben.<\/p>\n<p>Beides sind gute Anl&auml;sse, ein Tool zu programmieren, das den Kunden dabei unterst&uuml;tzt, die Datenbank in eine Form zu bringen, die keine nachvollziehbaren personenbezogenen Daten mehr enth&auml;lt.<\/p>\n<h2>Konkreter Anlass<\/h2>\n<p>Im konkreten Fall geht es viel weniger um die oben beschriebene Konstellation, als um die &auml;nderung der Personendaten zwecks Ver&ouml;ffentlichung der programmierten L&ouml;sung samt Beispieldaten zum Ausprobieren der Anwendung.<\/p>\n<p>Der erste Vorsitzende eines befreundeten Sportvereins ist n&auml;mlich mit einer Excel-Datei mit Mitgliedsdaten an mich herangetreten und hat mich gebeten, einmal eine vern&uuml;nftige Mitgliederverwaltung zu programmieren, mit der die Daten nicht mehr in einer einzigen Excel-Tabelle verwaltet werden m&uuml;ssen, sondern die es auch noch erm&ouml;glicht, zus&auml;tzliche Auswertungen zu produzieren.<\/p>\n<p>Wenn ich Ihnen als Leser von Access im Unternehmen meine Umsetzung dieser L&ouml;sung darlegen m&ouml;chte, muss ich also die Personendaten in dieser Excel-Tabelle vorher anonymisieren. Die L&ouml;sung mit der Umwandlung der Mitgliedsdatei in eine richtige Anwendung finden Sie &uuml;brigens unter dem Titel <b>Vereinsverwaltung: Von Excel zum Datenmodell <\/b>(<b>www.access-im-unternehmen.de\/1106<\/b>) in diesem Heft.<\/p>\n<h2>Konzept f&uuml;r das Tool<\/h2>\n<p>Wie aber gestalten wir nun das Tool, mit dem wir die Personendaten anonymisieren wollen Normalerweise w&uuml;rde ich es als Add-In programmieren. Allerdings m&uuml;sste der Kunde dann erst das Add-In installieren, was diesen m&ouml;glicherweise &uuml;berfordern k&ouml;nnte. Also gehen wir diesmal einen anderen Weg und programmieren eine eigene Access-L&ouml;sung, mit welcher der Kunde dann die betroffene Access-Anwendung ausw&auml;hlen soll. Im Formular dieser L&ouml;sung sollen dann die Tabellen der Ausgangsanwendung zur Auswahl angeboten werden. F&uuml;r die Felder der gew&auml;hlten Tabelle soll der Benutzer dann angeben, mit welchen Daten diese gef&uuml;llt werden sollen &#8211; also mit Vornamen, Nachnamen, Stra&szlig;en, PLZ und Ort, Land und so weiter. Dann soll er per Mausklick den Anonymisierungsvorgang starten k&ouml;nnen, welcher dann eine Kopie der Datenbank erstellt und die enthaltenen Daten &auml;ndert.<\/p>\n<h2>Das Tool erstellen<\/h2>\n<p>Als Tool legen wir also eine ganz normale Access-Datenbank an. Diese sollte beim &ouml;ffnen m&ouml;glichst gleich die enthaltene Funktion offenbaren, damit der Benutzer sich gleich zurechtfindet. In diesem Fall w&auml;re dies das &ouml;ffnen des Formulars, welches die Funktionen zum Ausw&auml;hlen der zu kopierenden und zu manipulierenden Datenbank enth&auml;lt.<\/p>\n<p>Das Formular soll keine Datens&auml;tze anzeigen, also stellen wir die Eigenschaften <b>Datensatzmarkierer<\/b>, <b>Navigationsschaltfl&auml;chen<\/b>, <b>Trennlinien <\/b>und <b>Bildlaufleisten <\/b>auf <b>Nein <\/b>ein.<\/p>\n<p>F&uuml;gen Sie im oberen Bereich zwei Textfelder namens <b>txtQuelldatei <\/b>und <b>txtZieldatei <\/b>ein sowie zwei Schaltfl&auml;chen namens <b>cmdDateiauswahl <\/b>und <b>cmdZieldateiFestlegen<\/b>. Diese ordnen Sie wie in Bild 1 an.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1112_001.png\" alt=\"Steuer-elemente zum Ausw&auml;hlen von Quell- und Zieldatei\" width=\"549,6265\" height=\"183,8407\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Steuer-elemente zum Ausw&auml;hlen von Quell- und Zieldatei<\/span><\/b><\/p>\n<p>F&uuml;r die <b>Beim Klicken<\/b>-Ereigniseigenschaft hinterlegen wir jeweils eine entsprechende Ereignisprozedur. Ein Klick auf die Schaltfl&auml;che <b>cmdDateiauswahl <\/b>l&ouml;st die Prozedur <b>cmdDateiauswahl_Click <\/b>aus Listing 1 aus. Diese verwendet die Funktion <b>Openfilename<\/b>, die Sie im Modul <b>mdlDateidialoge <\/b>finden. Sie &uuml;bergibt der Funktion den aktuellen Datenbankpfad als Parameter und stellt die Access-Dateiendungen <b>.mdb <\/b>und <b>.accdb <\/b>als Filter ein.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdDateiauswahl_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>strQuelldatei<span style=\"color:blue;\"> As String<\/span>\r\n     strQuelldatei = Openfilename(CurrentProject.Path, \"Quelldatei ausw&auml;hlen\", \"Access-DB (*.mdb;*.accdb)|Alle Dateien (*.*)\")\r\n     Me!txtQuelldatei = strQuelldatei\r\n     Me!txtZieldatei = VerzeichnisAusPfad(strQuelldatei) & \"\\\" & ZieldateiAusPfad(strQuelldatei)\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>cmdZieldateiFestlegen_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>strZieldatei<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strQuelldatei<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strZielverzeichnis<span style=\"color:blue;\"> As String<\/span>\r\n     strQuelldatei = Me!txtQuelldatei\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(Dir(strQuelldatei, vbDirectory)) &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n         strZieldatei = ZieldateiAusPfad(strQuelldatei)\r\n         strZielverzeichnis = VerzeichnisAusPfad(strQuelldatei)\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     strZieldatei = GetSaveFile(strZielverzeichnis, strZieldatei, \"Access-DB (*.mdb;*.accdb)|Alle Dateien (*.*)\", _\r\n         \"Zieldatei ausw&auml;hlen\")\r\n     Me!txtZieldatei = strZieldatei\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Function <\/span>VerzeichnisAusPfad(strPfad<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strVerzeichnis<span style=\"color:blue;\"> As String<\/span>\r\n     strVerzeichnis = <span style=\"color:blue;\">Left<\/span>(strPfad, <span style=\"color:blue;\">InStrRev<\/span>(strPfad, \"\\\") - 1)\r\n     VerzeichnisAusPfad = strVerzeichnis\r\n<span style=\"color:blue;\">End Function<\/span>\r\n<span style=\"color:blue;\">Private Function <\/span>ZieldateiAusPfad(strPfad<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strZieldatei<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>intPunkt<span style=\"color:blue;\"> As String<\/span>\r\n     strZieldatei = <span style=\"color:blue;\">Mid<\/span>(strPfad, <span style=\"color:blue;\">InStrRev<\/span>(strPfad, \"\\\") + 1)\r\n     intPunkt = <span style=\"color:blue;\">InStrRev<\/span>(strZieldatei, \".\")\r\n     strZieldatei = <span style=\"color:blue;\">Left<\/span>(strZieldatei, intPunkt - 1) & \"_Anonymisiert\" & <span style=\"color:blue;\">Mid<\/span>(strZieldatei, intPunkt)\r\n     ZieldateiAusPfad = strZieldatei\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Prozeduren zum Ausw&auml;hlen von Quell- und Zieldatei<\/span><\/b><\/p>\n<p>Das Ergebnis dieses Dialogs speichert sie in der Variablen, deren Inhalt direkt an das Textfeld <b>txtQuelldatei <\/b>weitergegeben wird. Au&szlig;erdem ruft die Prozedur zwei weitere Hilfsfunktionen namens <b>VerzeichnisAusPfad <\/b>und <b>ZieldateiAusPfad <\/b>auf, welche sich ebenfalls im Klassenmodul des Formulars befinden und im Listing abgebildet sind.<\/p>\n<p><b>VerzeichnisAusPfad <\/b>erwartet einen kompletten Pfad, also beispielsweise die Angabe aus Bild 2 im oberen Textfeld. Es ermittelt die Position des hintersten Backslash und liefert die Zeichenkette bis zu dieser Position minus eins, damit der Backslash wegf&auml;llt.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1112_002.png\" alt=\"Ausw&auml;hlen von Quell- und Zieldatei\" width=\"549,6265\" height=\"147,7122\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Ausw&auml;hlen von Quell- und Zieldatei<\/span><\/b><\/p>\n<p>Die Funktion <b>ZieldateiAusPfad <\/b>arbeitet etwas spezifischer und liefert nicht nur einfach den Dateinamen des hineingegebenen Pfades, sondern erg&auml;nzt diesen so, dass der Dateiname gut als die anonymisierte Version zu erkennen ist. Die Funktion ermittelt ebenfalls zun&auml;chst in einer Anweisung den Teil des mit <b>strPfad <\/b>&uuml;bergebenen Pfades, der sich hinter dem letzten Backslash in <b>strPfad <\/b>befindet. Das Verzeichnis wird also vorn abgeschnitten. Dann ermittelt sie die Position des Punktes, also des Zeichens, das sich zwischen dem eigentlichen Dateinamen und der Dateiendung befindet. Der in der Variablen <b>strZieldatei <\/b>zwischengespeicherte Ausdruck besteht dann aus dem Teil vor dem Punkt, dem Ausdruck <b>_anonymisiert. <\/b>und der Dateiendung. Dieses automatische F&uuml;llen des Feldes <b>txtZieldatei <\/b>ist eine M&ouml;glichkeit, dem Benutzer weitere Schritte abzunehmen, denn er braucht in den meisten F&auml;llen nicht noch die Zieldatei auszuw&auml;hlen.<\/p>\n<p>M&ouml;chte er dies dennoch tun, hat er nat&uuml;rlich die M&ouml;glichkeit dazu. Er braucht dann nur auf die Schaltfl&auml;che <b>cmdZieldateiFestlegen <\/b>zu klicken, welche die Prozedur <b>cmdZieldateiFestlegen_Click <\/b>ausl&ouml;st. Diese liest den Wert des Feldes <b>txtQuelldatei <\/b>in die Variable <b>strQuelldatei <\/b>ein und pr&uuml;ft, ob dieser Ausdruck ein g&uuml;ltiges Verzeichnis enth&auml;lt.<\/p>\n<p>In diesem Fall ermittelt die Prozedur &uuml;ber die Funktion <b>ZieldateiAusPfad <\/b>einen Vorschlag f&uuml;r den Namen der Zieldatei und speichert diesen in der Variablen <b>strZieldatei<\/b>. Das Zielverzeichnis wird &uuml;ber die Funktion <b>VerzeichnisAusPfad <\/b>ermittelt und landet in der Variablen <b>strZielverzeichnis<\/b>.<\/p>\n<p>Danach ruft die Prozedur &uuml;ber die Funktion <b>GetSaveFile <\/b>einen Dialog auf, &uuml;ber den der Benutzer die Zieldatei festlegen soll. Hier wird auch klar, warum wir zuvor Zielverzeichnis und Zieldatei in zwei verschiedene Variablen geschrieben haben: Beim Aufruf dieser Funktion wird das Zielverzeichnis als beim &ouml;ffnen anzuzeigendes Verzeichnis gew&auml;hlt und der Name der Zieldatei wird unten im Dialog voreingestellt (s. Bild 3). Schlie&szlig;lich landet die hier ausgew&auml;hlte Datei im Textfeld <b>txtZieldatei<\/b>.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1112_003.png\" alt=\"Ausw&auml;hlen der Zieldatei\" width=\"649,559\" height=\"463,1952\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Ausw&auml;hlen der Zieldatei<\/span><\/b><\/p>\n<h2>Datei kopieren<\/h2>\n<p>Wir haben ja avisiert, dass wir nicht in der vom Benutzer ausgew&auml;hlten Datei herumspielen &#8211; es kann immerhin sein, dass der Benutzer zuvor keine Kopie anlegt und direkt eine Originaldatei zum anonymisieren ausw&auml;hlt. Also legen wir auf jeden Fall zuvor eine Kopie der Datenbank an, wozu wir die zuvor ermittelten Daten zur Quell- und Zieldatei nutzen. <\/p>\n<p>Wir legen direkt einmal eine Schaltfl&auml;che namens <b>cmdAnonymisieren <\/b>im Formular an, auch wenn wir noch keine Steuer-elemente zum Einstellen der Anonymisierungsdetails hinzugef&uuml;gt habe und diese Funktionalit&auml;t noch fehlt. Allerdings ist das Kopieren der Datei der Grundstein, daher beginnen wir damit (s. Bild 4). Diese Schaltfl&auml;che l&ouml;st nun die folgende Prozedur aus, welche wiederum eine Funktion zum Kopieren der Quelldatenbank in die Zieldatenbank aufruft. Sollte diese Funktion namens <b>DateiKopieren <\/b>den Wert <b>True <\/b>zur&uuml;ckliefern, soll der Inhalt der <b>If&#8230;Then<\/b>-Bedingung ausgef&uuml;hrt werden:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1112_004.png\" alt=\"Schaltfl&auml;che zum Start des Vorgangs\" width=\"424,7115\" height=\"332,3455\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Schaltfl&auml;che zum Start des Vorgangs<\/span><\/b><\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdAnonymisieren_Click()\r\n     <span style=\"color:blue;\">If <\/span>DateiKopieren(Me!txtQuelldatei, Me!txtZieldatei)<span style=\"color:blue;\"> Then<\/span>\r\n         ''''...anonymisieren\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Funktion zum Kopieren der Datenbank<\/h2>\n<p>Die dadurch aufgerufene Funktion <b>DateiKopieren <\/b>finden sie in Listing 2. Die Funktion erwartet den Pfad der Quell- und der Zieldatenbank als Parameter. Das Kopieren gelingt nicht, wenn die Quelldatei ge&ouml;ffnet ist. Ob dies der Fall ist, ermitteln wir, indem wir nach einer Datei suchen, deren Dateiname auf <b>.ldb <\/b>oder <b>.laccdb <\/b>endet. Dies ist die Dateiendung der Datei, die beim &ouml;ffnen einer Access-Datei angelegt wird. Ist eine solche Datei vorhanden, dann ist die Quelldatei offensichtlich ge&ouml;ffnet und kann nicht kopiert werden. Der Versuch, dies mit der <b>FileCopy<\/b>-Funktion zu erledigen, w&uuml;rde sonst zum Ausl&ouml;sen des Fehlers mit der Nummer <b>70 <\/b>f&uuml;hren (<b>Zugriff verweigert<\/b>). In diesem Fall gibt die Funktion eine entsprechende Meldung aus. Auch f&uuml;r die Zieldatei erfolgt eine entsprechende &uuml;berpr&uuml;fung. Ist die Quelldatenbank hingegen nicht ge&ouml;ffnet und die Zieldatenbank entweder nicht ge&ouml;ffnet oder nicht vorhanden, kopiert die Prozedur diese mit der <b>FileCopy<\/b>-Methode, der Sie den Pfad zur Quell- und zur Zieldatei &uuml;bergibt.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>DateiKopieren(strQuelle<span style=\"color:blue;\"> As String<\/span>, strZiel<span style=\"color:blue;\"> As String<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>strQuelleTemp<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strZielTemp<span style=\"color:blue;\"> As String<\/span>\r\n     strQuelleTemp = <span style=\"color:blue;\">Replace<\/span>(strQuelle, \".mdb\", \".ldb\")\r\n     strQuelleTemp = <span style=\"color:blue;\">Replace<\/span>(strQuelleTemp, \".accdb\", \".laccdb\")\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(Dir(strQuelleTemp)) &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Die Quelldatei muss geschlossen sein.\"\r\n         <span style=\"color:blue;\">Exit Function<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     strZielTemp = <span style=\"color:blue;\">Replace<\/span>(strZiel, \".mdb\", \".ldb\")\r\n     strZielTemp = <span style=\"color:blue;\">Replace<\/span>(strZielTemp, \".accdb\", \".laccdb\")\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(Dir(strZielTemp)) &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Die Zieldatei muss geschlossen sein.\"\r\n         <span style=\"color:blue;\">Exit Function<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     FileCopy strQuelle, strZiel\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(Dir(strZiel)) &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n         DateiKopieren = <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 2: Die Funktion zum Kopieren der Zieldatei in die Quelldatei<\/span><\/b><\/p>\n<p>Ist die Zieldatei anschlie&szlig;end im Dateisystem vorhanden, stellt die Funktion den R&uuml;ckgabewert auf <b>True <\/b>ein.<\/p>\n<h2>Tabellen und Felder speichern<\/h2>\n<p>Wir wollen es dem Benutzer erm&ouml;glichen, alle zu &auml;ndernden Tabellen und Felder auf einen Streich zu definieren. Dazu muss er diese zun&auml;chst einlesen und wir m&uuml;ssen mit einem geeigneten Formular daf&uuml;r sorgen, dass diese auch vom Benutzer bearbeitet werden k&ouml;nnen.<\/p>\n<p>Wir wollen also Informationen &uuml;ber die Tabellen und die darin enthaltenen Felder speichern. Dazu ben&ouml;tigen wir zwei Tabellen namens <b>tblTabellen<\/b> und <b>tblFelder<\/b>. Die erste sieht im Entwurf wie in Bild 5 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1112_006.png\" alt=\"Entwurf der Tabelle tblTabellen\" width=\"599,593\" height=\"367,6131\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Entwurf der Tabelle tblTabellen<\/span><\/b><\/p>\n<p>Sie enth&auml;lt ein Prim&auml;rschl&uuml;sselfeld, ein Feld mit dem Namen der Tabelle sowie ein <b>Ja\/Nein<\/b>-Feld namens <b>Anonymisieren<\/b>, mit der Benutzer festlegen kann, ob diese Tabelle komplett anonymisiert werden soll. Die zweite Tabelle namens <b>tblFelder <\/b>enth&auml;lt einige Felder mehr. Das liegt daran, dass hier die Details zum Anonymisieren der Inhalte gespeichert werden. Den Entwurf der Tabelle k&ouml;nnen Sie Bild 6 entnehmen. Neben den bereits aus der Tabelle <b>tblTabellen <\/b>bekannten Feldern nutzen wir hier die folgenden Felder:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1112_008.png\" alt=\"Entwurf der Tabelle tblFelder\" width=\"599,593\" height=\"476,1472\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Entwurf der Tabelle tblFelder<\/span><\/b><\/p>\n<ul>\n<li><b>Felddatentyp<\/b>: Speichert den Zahlenwert, der den Felddatentyp des Feldes repr&auml;sentiert<\/li>\n<li><b>TabelleID<\/b>: Fremdschl&uuml;sselfeld, mit dem der Datensatz der Tabelle <b>tblTabellen <\/b>festgelegt wird, zu dem das Feld geh&ouml;rt<\/li>\n<li><b>ErsetzenMitID<\/b>: Fremdschl&uuml;sselfeld zu einer Tabelle namens <b>tblErsetzenMit<\/b>. Hier werden verschiedene M&ouml;glichkeiten zum Ersetzen abgebildet, zum Beispiel Vorname, Nachname, Stra&szlig;e et cetera. Damit wird beispielsweise festgelegt, aus welchen Hilfstabellen die zu ersetzenden Daten kommen.<\/li>\n<li><b>Info1<\/b>, <b>Info2<\/b>, <b>Info3<\/b>: Diese Felder enthalten Hinweistexte f&uuml;r weitere Informationen, die in den Feldern <b>Wert1<\/b>, <b>Wert2 <\/b>und <b>Wert3 <\/b>gespeichert werden. Damit k&ouml;nnen Sie zum Beispiel f&uuml;r Felder, die mit Datumswerten gef&uuml;llt werden sollen, den Datumsbereich festlegen.<\/li>\n<li><b>Wert1<\/b>, <b>Wert2<\/b>, <b>Wert3<\/b>: Diese Felder nehmen die Werte auf, die zum F&uuml;llen der Felder als Referenz benutzt werden sollen.<\/li>\n<\/ul>\n<p>Wie Sie der Abbildung entnehmen k&ouml;nnen, legen wir f&uuml;r die Tabelle au&szlig;erdem einen zusammengesetzten, eindeutigen Index an, der daf&uuml;r sorgt, dass jeder Feldname nur einmal je Tabelle eingegeben werden darf. Dazu f&uuml;gen wir diesem die beiden Felder <b>Feldname <\/b>und <b>TabelleID <\/b>hinzu und legen f&uuml;r die Eigenschaft <b>Eindeutig <\/b>den Wert <b>Ja <\/b>fest.<\/p>\n<h2>Beziehung zwischen den Tabellen tblTabellen und tblFelder<\/h2>\n<p>Die Tabelle <b>tblTabellen <\/b>und <b>tblFelder <\/b>sollen in einer 1:n-Beziehung stehen. Dazu haben wir per Nachschlagefeld bereits die notwendige Verkn&uuml;pfung hinzugef&uuml;gt. Die Verkn&uuml;pfung k&ouml;nnen Sie in Bild 7 einsehen. Hier definieren wir noch referenzielle Integrit&auml;t und aktivieren die <b>L&ouml;schweitergabe an verwandte Datens&auml;tze<\/b>. Dadurch werden beim L&ouml;schen eines der Datens&auml;tze der Tabelle <b>tblTabellen <\/b>auch alle mit diesem Datensatz verkn&uuml;pften Datens&auml;tze der Tabelle <b>tblFelder <\/b>gel&ouml;scht.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1112_005.png\" alt=\"Beziehung zwischen den beiden Tabellen tblTabellen und tblFelder\" width=\"649,559\" height=\"300,9539\" \/><\/p>\n<p><!--30percent--><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 7: Beziehung zwischen den beiden Tabellen tblTabellen und tblFelder<\/span><\/b><\/p>\n<h2>Die Tabelle tblErsetzenMit<\/h2>\n<p>Die Tabelle, die Informationen dar&uuml;ber liefert, wie die Daten eines Feldes ersetzt werden sollen, hei&szlig;t <b>tblErsetzenMit<\/b> (s. Bild 8). Diese enth&auml;lt neben dem Prim&auml;rschl&uuml;sselfeld ein Feld mit der Bezeichnung der Ersetzungsart sowie, genau wie die Tabelle <b>tblFelder<\/b>, die drei Felder <b>Info1<\/b>, <b>Info2 <\/b>und <b>Info3<\/b>. Diese werden mit Standardwerten bef&uuml;llt, die dann nach der Auswahl eines der Eintr&auml;ge f&uuml;r das Fremdschl&uuml;sselfeld <b>ErsetzenMitID <\/b>der Tabelle <b>tblFelder <\/b>in die entsprechenden Felder <b>Info1<\/b>, <b>Info2 <\/b>und <b>Info3 <\/b>&uuml;bertragen werden &#8211; gleiches gilt f&uuml;r die Felder <b>Wert1, Wert2 <\/b>und <b>Wert3<\/b>. Der Benutzer kann die Felder dann nach eigenen Vorstellungen anpassen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1112_009.png\" alt=\"Entwurf der Tabelle tblErsetzenMit\" width=\"499,6607\" height=\"456,9388\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 8: Entwurf der Tabelle tblErsetzenMit<\/span><\/b><\/p>\n<h2>Konfigurationen speichern<\/h2>\n<p>Wenn wir schon dar&uuml;ber reden, dass der Benutzer die Einstellungen f&uuml;r das Anonymisieren der Daten in den beiden Tabellen <b>tblTabellen <\/b>und <b>tblFelder <\/b>speichert, dann sollten wir uns auch Gedanken machen, was geschieht, wenn der Vorgang abgeschlossen ist. Erst hatten wir geplant, die Daten einfach zu l&ouml;schen. Allerdings ist dann klar geworden, dass es ja durchaus sein kann, dass der Benutzer nicht nur einmalig seine aktuelle Version der Datenbank anonymisieren und an einen Entwickler &uuml;bergeben m&ouml;chte. Also speichern wir die Konfigurationen in einer Tabelle namens <b>tblKonfigurationen<\/b> (s. Bild 9). Damit die gespeicherten Daten &uuml;ber die Tabellen und Felder auch der entsprechenden Konfiguration zugeordnet werden k&ouml;nnen, f&uuml;gen wir der Tabelle <b>tblTabellen <\/b>noch ein Fremdschl&uuml;sselfeld namens <b>KonfigurationID <\/b>auf die Tabelle <b>tblKonfigurationen <\/b>hinzu.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1112_007.png\" alt=\"Entwurf der Tabelle tblKonfigurationen\" width=\"499,6607\" height=\"358,1186\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 9: Entwurf der Tabelle tblKonfigurationen<\/span><\/b><\/p>\n<p>Die Tabelle <b>tblKonfigurationen <\/b>enth&auml;lt neben Prim&auml;rschl&uuml;sselfeld und Bezeichnung noch die beiden Felder <b>Quelldatenbank <\/b>und <b>Zieldatenbank<\/b>. Gleich werden wir noch das Formular <b>frmStart <\/b>so anpassen, dass dieses das Anlegen und Abrufen von Konfigurationen erm&ouml;glicht.<\/p>\n<h2>Formulare f&uuml;r die Festlegung von Tabellen und Feldern<\/h2>\n<p>Zuvor wollen wir jedoch noch die Unterformulare anlegen, die sp&auml;ter im Formular <b>frmStart <\/b>landen und die Tabellen und Felder der zu anonymisierenden Datenbank zur Konfiguration durch den Benutzer anzeige sollen. Die Tabellen und Felder sollen in einer hierarchischen Ansicht erscheinen, wobei wir diesmal kein TreeView verwenden wollen, sondern die Unterdatenblatt-Ansicht von Access.<\/p>\n<p>Wie Sie diese einrichten, erfahren Sie im Beitrag <b>Unterdatenbl&auml;tter in Formularen <\/b>(<b>www.access-im-unternehmen.de\/1108<\/b>) in dieser Ausgabe. Dort finden Sie sowohl die Beschreibung der Prozeduren, um die beiden Tabellen <b>tblTabellen <\/b>und <b>tblFelder <\/b>zu f&uuml;llen als auch die Anleitung, wie Sie die Daten der beiden Tabellen in Form eines Datenblatts mit Unterdatenblatt anzeigen. Schlie&szlig;lich zeigen wir dort auch noch, wie Sie die Kontrollk&auml;stchen f&uuml;r die <b>Ja\/Nein<\/b>-Felder <b>Anonymisieren <\/b>der beiden Tabellen so programmieren, dass die jeweils &uuml;ber- beziehungsweise untergeordneten Elemente aktiviert oder deaktiviert werden (mehr zu diesen Abh&auml;ngigkeiten im genannten Beitrag).<\/p>\n<p>Sobald sich nicht nur das Formular <b>frmStart<\/b>, sondern auch die beiden Unterformulare <b>sfmTabellen <\/b>und <b>sfmFelder <\/b>in Ihrer Datenbank befinden, k&ouml;nnen wir diese zusammenf&uuml;hren. Das Unterformular <b>sfmFelder <\/b>wurde ja bereits im Beitrag <b>Unterdatenbl&auml;tter in Formularen <\/b>in das Formular <b>sfmTabellen <\/b>integriert.<\/p>\n<p>Damit machen wir uns nun ans Werk, um den Entwurf des Formulars <b>frmStart<\/b> in den Zustand aus Bild 10 zu bringen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1112_010.png\" alt=\"Das Formular frmStart nach dem Umbau\" width=\"649,559\" height=\"566,972\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 10: Das Formular frmStart nach dem Umbau<\/span><\/b><\/p>\n<p>Dazu sind die folgenden Schritte n&ouml;tig:<\/p>\n<ul>\n<li>Einf&uuml;gen des Unterformulars <b>sfmTabellen <\/b>aus dem Navigationsbereich in den Entwurf des Formulars <b>frmStart<\/b><\/li>\n<li>Einstellen der Eigenschaften <b>Horizontaler Anker <\/b>und <b>Vertikaler Anker <\/b>f&uuml;r das entstandene Unterformular-Steuerelement auf <b>Beide <\/b>einstellen. Passend dazu f&uuml;r das Bezeichnungsfeld des Unterformulars die eigenschaften auf <b>Links <\/b>beziehungsweise <b>Oben <\/b>einstellen und f&uuml;r die Schaltfl&auml;che <b>cmdAnonymisieren <\/b>auf <b>Links <\/b>und <b>Unten<\/b>.<\/li>\n<li>Einstellen der Datenherkunft des Formulars <b>frmStart <\/b>auf die Tabelle <b>tblKonfigurationen<\/b>.<\/li>\n<li>Die beiden Textfelder <b>txtQuelldatenbank <\/b>und <b>txtZieldatenbank <\/b>k&ouml;nnen Sie nun an die entsprechenden Felder der Datenherkunft binden, indem Sie die Eigenschaft <b>Steuerelementinhalt <\/b>auf die Werte <b>Quelldatenbank <\/b>und <b>Zieldatenbank <\/b>einstellen.<\/li>\n<li>Au&szlig;erdem f&uuml;gen wir oben im Formular noch ein Kombinationsfeld namens <b>cboKonfigurationen <\/b>hinzu, das wir gleich im Anschluss anpassen.<\/li>\n<\/ul>\n<p>F&uuml;r die Eigenschaft <b>Datensatzherkunft <\/b>des Kombinationsfeldes geben wir die folgende SQL-Abfrage ein:<\/p>\n<pre>SELECT KonfigurationID, Konfiguration \r\nFROM tblKonfigurationen \r\nORDER BYKonfiguration;<\/pre>\n<p>Damit nur der Wert des Feldes <b>Konfiguration <\/b>angezeigt wird und nicht der Wert des Prim&auml;rschl&uuml;sselfeldes <b>KonfigurationID<\/b>, stellen wir die Eigenschaften <b>Spaltenanzahl <\/b>auf <b>2 <\/b>und <b>Spaltenbreiten <\/b>auf <b>0cm <\/b>ein.<\/p>\n<h2>Neue Konfiguration anlegen<\/h2>\n<p>Nun wollen wir eine neue Konfiguration anlegen. Dies wollen wir &uuml;ber das Kombinationsfeld <b>cboKonfigurationen <\/b>erledigen, indem wir diesem einen Eintrag mit dem Text <b><Ausw&auml;hlen> <\/b>und einen weiteren mit dem Text <b><Neue Konfiguration> <\/b>hinzuf&uuml;gen.<\/p>\n<p>Dazu erweitern wir die Datensatzherkunft wie folgt:<\/p>\n<pre>SELECT -1, \"&lt;Ausw&auml;hlen&gt;\" AS Konfiguration \r\nFROM MSysObjects \r\nUNION \r\nSELECT 0 , \"&lt;Neue Konfiguration&gt;\" AS Konfiguration \r\nFROM MSysObjects \r\nUNION \r\nSELECT KonfigurationID, Konfiguration \r\nFROM tblKonfigurationen \r\nORDER BY Konfiguration;<\/pre>\n<p>Da die Tabelle <b>tblKonfigurationen <\/b>zu Beginn gegebenenfalls keine Daten enth&auml;lt, m&uuml;ssen wir als Datenquelle der beiden erstgenannten Eintr&auml;ge eine Tabelle angeben, welche mindestens einen Datensatz liefert. Dies ist bei der Systemtabelle <b>MSysObjects <\/b>immer der Fall.<\/p>\n<p>Damit das Kombinationsfeld beim &ouml;ffnen des Formulars gleich den Eintrag <b><Ausw&auml;hlen> <\/b>anzeigt, legen wir eine Prozedur an, die durch das Ereignis <b>Beim Laden <\/b>des Formulars ausgel&ouml;st wird. Diese sieht wie folgt aus und stellt den Wert des Kombinationsfeldes auf &#8211;<b>1 <\/b>ein &#8211; also den Wert des ersten Eintrags der Datensatzherkunft:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     Me!cboKonfigurationen = -1\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Dadurch zeigt das Kombinationsfeld wie in Bild 11 direkt den Eintrag <b><Ausw&auml;hlen> <\/b>an. Wenn der Benutzer nun den Eintrag <b><Neue Konfiguration> <\/b>ausw&auml;hlt, soll eine <b>InputBox <\/b>erscheinen, in die der Benutzer den Namen der neuen Konfiguration eintragen kann. Dazu hinterlegen wir f&uuml;r die Ereignisprozedur, die durch das Ereignis <b>Nach Aktualisierung <\/b>des Kombinationsfeldes ausgel&ouml;st wird, die Prozedur aus Listing 3.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1112_011.png\" alt=\"Auswahl einer neuen Konfiguration\" width=\"549,6265\" height=\"154,1982\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 11: Auswahl einer neuen Konfiguration<\/span><\/b><\/p>\n<p>Die Prozedur pr&uuml;ft in einer <b>Select Case<\/b>-Bedingung den Wert des Kombinationsfeldes <b>cboKonfigurationen<\/b>. Im Falle des Wertes <b>0 <\/b>hat der Benutzer den Eintrag <b><Neue Konfiguration> <\/b>ausgew&auml;hlt. In diesem Fall erscheint eine <b>InputBox<\/b>, die den Benutzer nach der Bezeichnung f&uuml;r die Konfiguration fragt. Hat der Benutzer einen anderen Wert als den Standardwert <b>[Neue Konfiguration] <\/b>eingeben und nicht die <b>Abbrechen<\/b>-Schaltfl&auml;che bet&auml;tigt (dies wird durch den Vergleich mit der leeren Zeichenkette gepr&uuml;ft), wird der innere Teil der ersten <b>If&#8230;Then<\/b>-Bedingung abgearbeitet. Hier f&uuml;llt die Prozedur die Variable <b>db <\/b>mit einem Verweis auf die aktuelle Datenbank. Danach tr&auml;gt sie mit einer <b>INSERT INTO<\/b>-Abfrage einen neuen Datensatz in die Tabelle <b>tblKonfigurationen <\/b>ein, welcher die angegebene Bezeichnung tr&auml;gt. Danach ermittelt die Prozedur mit der Abfrage SELECT @@IDENTITY den Prim&auml;rschl&uuml;sselwert des neu hinzugef&uuml;gten Datensatzes und speichert diesen in der Variablen <b>lngKonfigurationID<\/b>. Die <b>Requery<\/b>-Methode des Kombinationsfeldes <b>cboKonfigurationen <\/b>aktualisiert die im Kombinationsfeld verf&uuml;gbaren Daten, wodurch nun auch der soeben hinzugef&uuml;gte Datensatz ausgew&auml;hlt werden kann. Dann stellt die Prozedur das Kombinationsfeld auf den neuen Datensatz der Tabelle <b>tblKonfigurationen <\/b>ein. Auch die Datenherkunft des Formulars <b>frmStart <\/b>aktualisiert die Prozedur mit der <b>Requery<\/b>-Methode. Au&szlig;erdem stellt sie eine Variable namens <b>bolGeaendert <\/b>auf den Wert <b>True <\/b>ein.<\/p>\n<p>Im Falle der Auswahl des Eintrags <b><Ausw&auml;hlen> <\/b>des Kombinationsfeldes, was dem Wert <b>-1 <\/b>entspricht, ruft die Prozedur eine weitere Routine namens <b>SteuerelementeEinstellen <\/b>auf und &uuml;bergibt als Parameter den Wert <b>False<\/b>. Diese Prozedur schauen wir uns weiter unten an. In allen anderen F&auml;llen stellt die <b>Select Case<\/b>-Bedingung einfach den Wert von <b>bolGeaendert <\/b>auf den Wert <b>True <\/b>ein.<\/p>\n<p>Im letzten Schritt untersucht die Prozedur den Wert der Variablen <b>bolGeaendert<\/b>. Entspricht dieser dem Wert <b>True<\/b>, dann wird ebenfalls die Prozedur <b>SteuerelementeEinstellen <\/b>aufgerufen &#8211; diesmal allerdings mit dem Parameter <b>True<\/b>.<\/p>\n<p>Die Prozedur <b>SteuerelementeEinstellen <\/b>finden Sie in Listing 4. Sie pr&uuml;ft, ob der beim Aufruf &uuml;bergebene Parameter <b>bolAuswahl <\/b>den Wert <b>True <\/b>oder <b>False <\/b>liefert. Davon abh&auml;ngig stellt sie die Eigenschaften einiger Steuer-elemente ein. Wenn der Benutzer einen neuen Eintrag angelegt oder einen vorhandenen Datensatz ausgew&auml;hlt hat, soll das aktuelle Formular die Tabelle <b>tblKonfigurationen <\/b>als Datenquelle erhalten und der Datensatz dieser Tabelle angezeigt werden, dessen Feld <b>KonfigurationID <\/b>mit dem Wert des gew&auml;hlten Eintrags im Kombinationsfeld &uuml;bereinstimmt. Das <b>Unterformular<\/b>-Steuerelement wird &uuml;ber die Eigenschaft <b>SourceObject <\/b>mit dem Unterformular <b>sfmTabellen <\/b>gef&uuml;llt. Die beiden Eigenschaften <b>Verkn&uuml;pfen von <\/b>und <b>Verkn&uuml;pfen nach <\/b>(<b>LinkChildFields <\/b>und <b>LinkMasterFields<\/b>) werden mit dem Feldnamen <b>KonfigurationID <\/b>gef&uuml;llt, damit das Unterformular die zum aktuellen Datensatz des Hauptformulars passenden Tabellen anzeigt.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>SteuerelementeEinstellen(bolAuswahl<span style=\"color:blue;\"> As Boolean<\/span>)\r\n     <span style=\"color:blue;\">If <\/span>bolAuswahl<span style=\"color:blue;\"> Then<\/span>\r\n         Me.RecordSource = \"tblKonfigurationen\"\r\n         Me.Recordset.FindFirst \"KonfigurationID = \" & Me!cboKonfigurationen\r\n         Me!sfmTabellen.SourceObject = \"sfmTabellen\"\r\n         Me!sfmTabellen.LinkChildFields = \"KonfigurationID\"\r\n         Me!sfmTabellen.LinkMasterFields = \"KonfigurationID\"\r\n         Me!txtQuelldatei.Enabled = <span style=\"color:blue;\">True<\/span>\r\n         Me!txtQuelldatei.ControlSource = \"Quelldatenbank\"\r\n         Me!txtZieldatei.Enabled = <span style=\"color:blue;\">True<\/span>\r\n         Me!txtZieldatei.ControlSource = \"Zieldatenbank\"\r\n         Me!cmdDateiauswahl.Enabled = <span style=\"color:blue;\">True<\/span>\r\n         Me!cmdZieldateiFestlegen.Enabled = <span style=\"color:blue;\">True<\/span>\r\n         Me!cmdTabellenUndFelderEinlesen.Enabled = <span style=\"color:blue;\">True<\/span>\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n         Me!sfmTabellen.LinkMasterFields = \"\"\r\n         Me!sfmTabellen.LinkChildFields = \"\"\r\n         <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n         Me!sfmTabellen.SourceObject = \"\"\r\n         Me!txtQuelldatei.Enabled = <span style=\"color:blue;\">False<\/span>\r\n         Me!txtQuelldatei.ControlSource = \"\"\r\n         Me!txtZieldatei.Enabled = <span style=\"color:blue;\">False<\/span>\r\n         Me!txtZieldatei.ControlSource = \"\"\r\n         Me!cmdDateiauswahl.Enabled = <span style=\"color:blue;\">False<\/span>\r\n         Me!cmdZieldateiFestlegen.Enabled = <span style=\"color:blue;\">False<\/span>\r\n         Me!cmdTabellenUndFelderEinlesen.Enabled = <span style=\"color:blue;\">False<\/span>\r\n         Me.RecordSource = \"\"\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 4: Einstellen einiger Steuerelementeigenschaften<\/span><\/b><\/p>\n<p>Die beiden Textfelder <b>txtQuelldatei <\/b>und <b>txtZieldatei <\/b>werden aktiviert und ihr Steuerelementinhalt &uuml;ber die Eigenschaft <b>ControlSource <\/b>auf die Felder <b>Quelldatenbank <\/b>beziehungsweise <b>Zieldatenbank <\/b>eingestellt.<\/p>\n<p>Die drei Schaltfl&auml;chen <b>cmdDateiauswahl<\/b>, <b>cmdZieldateiFestlegen <\/b>und <b>cmdTabellenUndFelderEinlesen <\/b>werden aktiviert.<\/p>\n<p>Andersherum l&auml;uft es, wenn der Benutzer beispielsweise wieder den Wert <b><Ausw&auml;hlen> <\/b>im Kombinationsfeld ausw&auml;hlt, oder wenn das Formular gerade ge&ouml;ffnet wird und dieser Eintrag ohnehin automatisch ausgew&auml;hlt wird. Dies geschieht ja in der Ereignisprozedur <b>Form_Load<\/b>, der wir noch den folgenden Aufruf der Prozedur <b>SteuerelementeEinstellen <\/b>zuweisen:<\/p>\n<pre>SteuerelementeEinstellen <span style=\"color:blue;\">False<\/span><\/pre>\n<p>Mit dem Parameter <b>False <\/b>leert die Prozedur die Eigenschaften <b>Verkn&uuml;pfen von <\/b>und <b>Verkn&uuml;pfen nach <\/b>sowie <b>SourceObject <\/b>des Unterformular-Steuerelements. Dieses ist damit leer. Eine andere L&ouml;sung gibt es nicht &#8211; wenn wir die Eigenschaften beibehalten, wird ein leere Datensatz angezeigt, was wir nicht wollen.<\/p>\n<p>Au&szlig;erdem werden die &uuml;brigen Steuer-elemente wieder aktiviert und mit den Steuerelementinhalten gef&uuml;llt. Direkt nach dem &ouml;ffnen sieht das Formular <b>frmStart<\/b> wie in Bild 12 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1112_012.png\" alt=\"Formular frmStart vor der Auswahl der zu bearbeitenden Konfiguration\" width=\"599,593\" height=\"257,4828\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 12: Formular frmStart vor der Auswahl der zu bearbeitenden Konfiguration<\/span><\/b><\/p>\n<h2>Tabellen und Felder einlesen<\/h2>\n<p>Die bereits im Beitrag Unterdatenbl&auml;tter in Formularen vorgestellte Prozedur <b>TabellenUndFelderErmitteln <\/b>rufen wir durch einen Mausklick auf die Schaltfl&auml;che <b>cmdTabellenUndFelderEinlesen <\/b>auf (s. Bild 13):<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1112_013.png\" alt=\"Formular frmStart mit geladenen Tabellen und Feldern\" width=\"599,593\" height=\"489,7575\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 13: Formular frmStart mit geladenen Tabellen und Feldern<\/span><\/b><\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdTabellenUndFelderEinlesen_Click()\r\n     TabellenUndFelderErmitteln Me!txtQuelldatei\r\n     Me!sfmTabellen.Form.Requery\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Anonymisieren der Daten<\/h2>\n<p>Damit kommen wir zum letzten und  aufw&auml;ndigsten Schritt. Wir m&uuml;ssen die Tabelle <b>tblErsetzenMit <\/b>mit den verschiedenen Datentypen f&uuml;llen, f&uuml;r die wir Ersetzungsalgorithmen anbieten wollen. Au&szlig;erdem m&uuml;ssen wir diese Algorithmen nat&uuml;rlich auch noch programmieren.<\/p>\n<p>Man kann dies in zwei verschiedenen Auspr&auml;gungen durchf&uuml;hren. Die erste durchl&auml;uft einfach das Feld, dessen Inhalt anonymisiert werden soll, f&uuml;r alle Datens&auml;tze und ersetzt den Wert mit einem zuf&auml;llig aus einer daf&uuml;r vorgesehenen Tabelle ermittelten neuen Wert.<\/p>\n<p>So k&ouml;nnten wir beispielsweise die Vornamen einer Kundentabelle einfach ersetzen.<\/p>\n<p>Das reicht f&uuml;r die meisten Anwendungen vermutlich aus. In manchen F&auml;llen kann es allerdings vorkommen, dass beispielsweise alle Datens&auml;tze, die einen bestimmten Nachnamen aufweisen, auch nach dem Anonymisieren wieder den gleichen Nachnamen haben.<\/p>\n<p>Wenn man noch einen Schritt weitergeht, k&ouml;nnte man auch noch ber&uuml;cksichtigen, dass die zum Anonymisieren ersetzten Daten schl&uuml;ssig sind, also etwa die Postleitzahl und der Ort immer zusammenpassen &#8211; oder auch die Anrede und der Vorname des jeweiligen Kunden. Allerdings wollen wir es im Rahmen dieses Beitrags bei der einfachen Variante belassen, welche einfach die Daten ersetzt.<\/p>\n<h2>Vornamen ersetzen<\/h2>\n<p>Wir wollen als Erstes die Vornamen in der zu anonymisierenden Anwendung ersetzen. Auch hier gibt es wieder verschiedene M&ouml;glichkeiten. Die erste ist, einfach irgendwelche per Zufallsgenerator ermittelten Zeichenfolgen als Namen anzugeben. Also wollen wir dies einmal ausprobieren. Dazu legen wir einen allgemeinen Zufallsgenerator f&uuml;r Texte an. Zuerst f&uuml;gen Sie dazu einen neuen Datensatz zur Tabelle <b>tblErsetzenMit <\/b>hinzu, der wie in Bild 14 aussieht. Das Feld <b>ErsetzenMit <\/b>nimmt die Bezeichnung auf. Die Felder <b>Info1<\/b>, <b>Info2 <\/b>und <b>Info3 <\/b>erhalten Beschreibungstexte der in den Feldern <b>Wert1<\/b>, <b>Wert2 <\/b>und <b>Wert3 <\/b>gespeicherten Angaben. In diesem Fall soll das erste Feld (<b>Feld1<\/b>) die Mindestanzahl der Wortl&auml;nge f&uuml;r die zu erstellende Zeichenkette erhalten und <b>Wert2 <\/b>die maximale Anzahl. <b>Wert3 <\/b>gibt an, ob das erste Zeichen gro&szlig; und die &uuml;brigen Zeichen klein geschrieben werden sollen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1112_014.png\" alt=\"Parameter zum Ersetzen eines Feldinhaltes per Zufallszeichenkette\" width=\"700\" height=\"137,6499\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 14: Parameter zum Ersetzen eines Feldinhaltes per Zufallszeichenkette<\/span><\/b><\/p>\n<p>Diese Werte sollen nun nach der Auswahl des Eintrags Zufallszeichenkette f&uuml;r eines der Felder, hier f&uuml;r das Feld Vorname der Tabelle <b>tblPersonal<\/b>, in den entsprechenden Datensatz der Tabelle <b>tblFelder <\/b>&uuml;bertragen werden. Dazu hinterlegen wir f&uuml;r das Steuer-element <b>cboErsetzenMitID <\/b>(dem wir zuvor noch das Pr&auml;fix <b>cbo <\/b>zum Namen hinzuf&uuml;gen) eine Ereignisprozedur, die durch das Ereignis <b>Nach Aktualisierung <\/b>ausgel&ouml;st wird (s. Listing 5).<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cboErsetzenMitID_AfterUpdate()\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>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT * FROM tblErsetzenMit WHERE ErsetzenMitID = \" & Me!cboErsetzenMitID, dbOpenDynaset)\r\n     Me!Wert1 = rst!Wert1\r\n     Me!Wert2 = rst!Wert2\r\n     Me!Wert3 = rst!Wert3\r\n     Me!Info1 = rst!Info1\r\n     Me!Info2 = rst!Info2\r\n     Me!Info3 = rst!Info3 \r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 5: Einf&uuml;gen der Parameter f&uuml;r die Ersetzungsmethode<\/span><\/b><\/p>\n<p>Das Ergebnis soll wie in Bild 15 aussehen &#8211; die Werte der Felder <b>Wert1<\/b>, <b>Wert2 <\/b>und <b>Wert3 <\/b>der Tabelle <b>tblErsetzenMit <\/b>werden in den entsprechenden Datensatz der Tabelle <b>tblFelder <\/b>eingetragen. Hilfreich w&auml;re es nun noch, wenn wir den Benutzer informieren, welche Funktion die einzelnen Parameter haben. Der Steuerelement-Tiptext funktioniert leider in der Datenblattansicht nicht, sonst h&auml;tten wir das jeweilige Feld immer mit einen passenden Hinweistext versehen k&ouml;nnen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1112_015.png\" alt=\"Auswahl des Ersetzungstyps\" width=\"599,593\" height=\"440,2671\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 15: Auswahl des Ersetzungstyps<\/span><\/b><\/p>\n<p>Also f&uuml;gen wir unterhalb des Unterformulars <b>sfmTabellen <\/b>ein Bezeichnungsfeld hinzu, das weitere Informationen zu den Werten liefert. Dieses nennen wir <b>lblInfos<\/b>. Es soll eine etwas kleinere Schriftart erhalten als vorgegeben (zum Beispiel <b>8<\/b>) und f&uuml;r die Anzeige von bis zu drei Zeilen ausgelegt sein. Stellen Sie auch die Eigenschaften <b>Horizontaler Anker <\/b>auf <b>Beide <\/b>und <b>Vertikaler Anker <\/b>auf <b>Unten <\/b>ein (s. Bild 16).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1112_016.png\" alt=\"Bezeichnungsfeld zum Ausgeben der Informationen zu den drei Feldern Wert1, Wert2 und Wert3.\" width=\"599,593\" height=\"231,3322\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 16: Bezeichnungsfeld zum Ausgeben der Informationen zu den drei Feldern Wert1, Wert2 und Wert3.<\/span><\/b><\/p>\n<p>Damit dieses Bezeichnungsfeld auch wie in Bild 17 die gew&uuml;nschten Informationen anzeigt, ben&ouml;tigen wir noch eine Prozedur, die ausgel&ouml;st wird, wenn der Benutzer einen der Datens&auml;tze des Unterformulars <b>sfmFelder<\/b> anklickt.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1112_017.png\" alt=\"Ausgeben der Informationen zu den drei Feldern Wert1, Wert2 und Wert3 nach dem Anklicken eines der Datens&auml;tze des Unterformulars\" width=\"599,593\" height=\"395,4761\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 17: Ausgeben der Informationen zu den drei Feldern Wert1, Wert2 und Wert3 nach dem Anklicken eines der Datens&auml;tze des Unterformulars<\/span><\/b><\/p>\n<p>Dies l&ouml;st das Ereignis <b>Beim Anzeigen <\/b>des Unterformulars aus, welches wir wie in Listing 6 f&uuml;llen. Die Ereignisprozedur pr&uuml;ft zun&auml;chst ob f&uuml;r diesen Datensatz &uuml;berhaupt ein Eintrag im Kombinationsfeld <b>cboErsetzenMitID <\/b>ausgew&auml;hlt ist. Falls ja, setzt sie den Wert f&uuml;r die Eigenschaft <b>Caption <\/b>des Bezeichnungsfeldes <b>lblInfos <\/b>mit einer etwas unkonventionell aufgebauten <b>DLookup<\/b>-Funktion zusammen. Der interessante Teil dabei ist der erste Parameter, der wie folgt lautet:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Current()\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> Nz(Me!cboErsetzenMitID, 0) = 0<span style=\"color:blue;\"> Then<\/span>\r\n         Me.Parent.Parent!lblInfos.Caption = DLookup(\"''''Wert1: '''' & Info1 & Chr(13) & Chr(10) & ''''Wert2: '''' & Info2 _\r\n             & Chr(13) & Chr(10) & ''''Wert3: '''' & Info3\", \"tblFelder\", \"FeldID = \" & Me!FeldID)\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         Me.Parent.Parent!lblInfos.Caption = \"\"\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 6: Anzeigen der Hilfetexte zu den drei Textfeldern Wert1, Wert2 und Wert3<\/span><\/b><\/p>\n<pre>''''Wert1: '''' & Info1 & Chr(13) & Chr(10) & ''''Wert2: '''' & Info2 & Chr(13) & Chr(10) & ''''Wert3: '''' & Info3<\/pre>\n<p>Damit wird eine Zeichenkette zusammengestellt, welche sowohl Literale als auch Feldinhalte der im zweiten Parameter angegebenen Tabelle enth&auml;lt. Zus&auml;tzlich kommen noch Zeilenumbr&uuml;che hinzu, die mit <b>Chr(13) &#038; Chr(10) <\/b>moduliert werden.<\/p>\n<h2>Vornamen durch Zufallszeichenfolgen ersetzen<\/h2>\n<p>Damit kommen wir endlich zu der Schaltfl&auml;che <b>cmdAnonymisieren<\/b>, mit der wir nun die Daten des Feldes <b>Vorname <\/b>der Tabelle <b>tblPersonal <\/b>durch Zufallszeichenketten ersetzen wollen. Dazu legen wir zuerst die Funktion an, welche die betroffenen Daten liefert.<\/p>\n<p>Dies erledigen wir in einem neuen Standardmodul namens <b>mdlAnonymisieren<\/b>. Die erste Funktion, die wir dort anlegen, hei&szlig;t <b>Zufallszeichenkette <\/b>(s. Listing 7). Sie erwartet drei Parameter:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>Zufallszeichenkette(intMindestlaenge<span style=\"color:blue;\"> As Integer<\/span>, intMaximaleLaenge<span style=\"color:blue;\"> As Integer<\/span>, _\r\n         bolErstesZeichenGross<span style=\"color:blue;\"> As Boolean<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>intAnzahlZeichen<span style=\"color:blue;\"> As Integer<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strZeichenkette<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>i<span style=\"color:blue;\"> As Integer<\/span>\r\n     intAnzahlZeichen = intMindestlaenge + Int(Rnd() * (intMaximaleLaenge - intMindestlaenge + 1))\r\n     <span style=\"color:blue;\">If <\/span>bolErstesZeichenGro&szlig; = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         strZeichenkette = Chr(65 + Int(Rnd() * 26))\r\n         intAnzahlZeichen = intAnzahlZeichen - 1\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     For i = 1 To intAnzahlZeichen\r\n         strZeichenkette = strZeichenkette & Chr(97 + Int(Rnd() * 26))\r\n     <span style=\"color:blue;\">Next<\/span> i\r\n     Zufallszeichenkette = strZeichenkette\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 7: Funktion zum Ermitteln von Zufallszeichenfolgen<\/span><\/b><\/p>\n<ul>\n<li><b>intMindestlaenge<\/b>: Mindestl&auml;nge f&uuml;r die Zeichenkette<\/li>\n<li><b>intMaximaleLaenge<\/b>: Maximale L&auml;nge f&uuml;r die Zeichenkette<\/li>\n<li><b>bolErstesZeichenGro&szlig;<\/b>: Gibt an, ob das erste Zeichen gro&szlig;geschrieben werden soll.<\/li>\n<\/ul>\n<p>Die Prozedur ermittelt zun&auml;chst per Zufall die L&auml;nge der zu erstellenden Zeichenfolge. Wenn der Parameter <b>intMindestlaenge <\/b>etwa den Wert <b>7 <\/b>und der Parameter <b>intMaximaleLaenge <\/b>den Wert <b>10 <\/b>enth&auml;lt, soll <b>intAnzahlZeichen <\/b>einen Wert zwischen <b>7 <\/b>und <b>10 <\/b>erhalten. Dazu addieren wir zu <b>intMindestlaenge <\/b>einen mit der Funktion <b>Rnd <\/b>zuf&auml;llig ermittelten Wert im Bereich von <b>0 <\/b>bis <b>3<\/b>. Das wir mit <b>Int(Rnd() * (intMaximaleLaenge &#8211; intMindestlaenge + 1)) <\/b>erhalten, liegt daran, dass <b>Rnd() <\/b>einen Wert zwischen <b>0 <\/b>und <b>1 <\/b>liefert, der mit der Differenz von <b>intMaximaleLaenge <\/b>und <b>intMindestlaenge <\/b>(hier <b>3<\/b>) multipliziert und durch die <b>Int<\/b>-Funktion von seinen Nachkommastellen befreit wird.<\/p>\n<p>Nachdem wir wissen, wieviele Zeichen der Ausdruck enthalten soll, pr&uuml;fen wir anhand des Parameters <b>bolErstesZeichenGross<\/b>, ob der erste Buchstabe gro&szlig;geschrieben werden soll. Falls ja, ermitteln wir mit der <b>Chr<\/b>-Funktion in Kombination mit <b>Int <\/b>und <b>Rnd <\/b>den ersten Buchstaben. <b>Rnd <\/b>liefert wie oben einen Wert zwischen <b>0 <\/b>und <b>1<\/b>, den wir mit <b>26 <\/b>multiplizieren und mit <b>Int <\/b>um die Nachkommastellen erleichtern. Dies ergibt eine Zahl zwischen <b>0 <\/b>und <b>25<\/b>. Diese addieren wir zur Zahl <b>65 <\/b>hinzu. <b>65 <\/b>liefert als Parameter der <b>Chr<\/b>-Funktion den Buchstaben <b>A<\/b>, die weiteren Zahlenwerte liefern die entsprechenden folgenden Buchstaben.<\/p>\n<p>Die Anzahl der Zeichen aus <b>intAnzahlZeichen <\/b>verringern wir in diesem Fall um den Wert <b>1<\/b>, da wir ja das erste Zeichen schon in <b>strZeichenkette <\/b>geschrieben haben.<\/p>\n<p>Danach folgen die &uuml;brigen Zeichen, die wir in einer <b>For&#8230;Next<\/b>-Schleife &uuml;ber die Zahlen von <b>1 <\/b>bis <b>intAnzahlZeichen <\/b>zusammensetzen. Dabei ermitteln wir die kleingeschriebenen Buchstaben auf &auml;hnliche Weise wie den gro&szlig;en Buchstaben von oben &#8211; mit dem Unterschied, dass wir der Funktion <b>Chr <\/b>nun die per Zufall ermittelten Werte von <b>97 <\/b>bis <b>122 <\/b>&uuml;bergeben.<\/p>\n<p>Schlie&szlig;lich geben wir die ermittelte Zufallszeichenkette mit dem Funktionswert zur&uuml;ck. Diese Funktion dient als erstes Beispiel f&uuml;r m&ouml;gliche Funktionen zum Anonymisieren von Zeichenketten.<\/p>\n<h2>Die Schaltfl&auml;che cmdAnonymisieren<\/h2>\n<p>Damit kommen wir endlich zur Funktion der Schaltfl&auml;che <b>cmdAnonymisieren<\/b>, welche die Zieldatenbank entsprechend unserer Vorgaben anonymisieren soll.<\/p>\n<p>Die durch diese Schaltfl&auml;che ausgel&ouml;ste Prozedur finden Sie in Listing 8. Sie deklariert gleich mehrere <b>Database<\/b>&#8211; und <b>Recordset<\/b>-Objekte:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdAnonymisieren_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>dbAnonymisieren<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>rstAnonymisieren<span style=\"color:blue;\"> As <\/span>DAO.Recordset\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>rstTabellen<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>rstFelder<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>strSQL<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">If <\/span>DateiKopieren(Me!txtQuelldatei, Me!txtZieldatei)<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> dbAnonymisieren = OpenDatabase(Me!txtZieldatei)\r\n         <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n         <span style=\"color:blue;\">Set<\/span> rstTabellen = db.OpenRecordset(\"SELECT DISTINCT tblTabellen.TabelleID, tblTabellen.Tabellenname \" _\r\n             & \"FROM tblTabellen INNER JOIN tblFelder ON tblTabellen.TabelleID = tblFelder.TabelleID \" _\r\n             & \"WHERE tblFelder.Anonymisieren = True\", dbOpenDynaset)\r\n         <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstTabellen.EOF\r\n             <span style=\"color:blue;\">Set<\/span> rstFelder = db.OpenRecordset(\"SELECT * FROM tblFelder WHERE Anonymisieren = TRUE AND TabelleID = \" _\r\n                 & rstTabellen!TabelleID, dbOpenDynaset)\r\n             <span style=\"color:blue;\">Set<\/span> rstAnonymisieren = dbAnonymisieren.OpenRecordset(\"SELECT * FROM \" & rstTabellen!Tabellenname, _\r\n                 dbOpenDynaset)\r\n             <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstAnonymisieren.EOF\r\n                 rstAnonymisieren.Edit\r\n                 <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstFelder.EOF\r\n                     Select Case rstFelder!ErsetzenMitID\r\n                         <span style=\"color:blue;\">Case <\/span>1\r\n                             rstAnonymisieren(rstFelder!Feldname) = Zufallszeichenkette(rstFelder!Wert1, _\r\n                                 rstFelder!Wert2, rstFelder!Wert3)\r\n                         <span style=\"color:blue;\">Case Else<\/span>\r\n                             <span style=\"color:blue;\">Debug.Print<\/span> rstFelder!ErsetzenMitID\r\n                     <span style=\"color:blue;\">End Select<\/span>\r\n                     rstFelder.Move<span style=\"color:blue;\">Next<\/span>\r\n                 <span style=\"color:blue;\">Loop<\/span>\r\n                 rstFelder.MoveFirst\r\n                 rstAnonymisieren.Update\r\n                 rstAnonymisieren.Move<span style=\"color:blue;\">Next<\/span>\r\n             <span style=\"color:blue;\">Loop<\/span>\r\n             rstTabellen.Move<span style=\"color:blue;\">Next<\/span>\r\n         <span style=\"color:blue;\">Loop<\/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: Anonymisieren von Textfeldern durch Zufallszeichenketten<\/span><\/b><\/p>\n<ul>\n<li><b>db<\/b>: Referenziert die aktuelle Datenbank.<\/li>\n<li><b>dbAnonymisieren<\/b>: Referenziert die Zieldatenbank.<\/li>\n<li><b>rstTabellen<\/b>: Referenziert die Tabelle <b>tblTabellen <\/b>der aktuellen Datenbank.<\/li>\n<li><b>rstFelder<\/b>: Referenziert die Tabelle <b>tblFelder <\/b>der aktuellen Datenbank.<\/li>\n<li><b>rstAnonymisieren<\/b>: Referenziert die Tabelle, deren Daten anonymisiert werden sollen.<\/li>\n<\/ul>\n<p>Die Prozedur ruft als Erstes die Funktion <b>DateiKopieren <\/b>auf, die wir bereits weiter oben vorgestellt haben. Liefert diese den Wert <b>True<\/b> zur&uuml;ck, &ouml;ffnet die Prozedur die neu erstellte Datenbank mit der <b>OpenDatabase<\/b>-Methode und referenziert diese mit der Variablen <b>dbAnonymisieren<\/b>. Auch die aktuelle Datenbank referenziert sie, diesmal mit der Variablen <b>db<\/b>. Dann folgt das Recordset <b>rstTabellen<\/b>, welches alle Datens&auml;tze der Tabelle <b>tblTabellen <\/b>aufnehmen soll, f&uuml;r es in der verkn&uuml;pften Tabelle <b>tblFelder <\/b>mindestens einen Datensatz gibt, dessen Feld <b>Anonymisieren <\/b>den Wert <b>True <\/b>aufweist.<\/p>\n<p>Die Datens&auml;tze dieses Recordsets durchl&auml;uft die Prozedur dann in einer <b>Do While<\/b>-Schleife. Innerhalb dieser Schleife &ouml;ffnet sie ein Recordset auf Basis der Tabelle <b>tblFelder<\/b>, die alle Datens&auml;tze enth&auml;lt, die zum aktuell in <b>rstTabellen <\/b>befindlichen Eintrag der Tabelle <b>tblTabellen <\/b>geh&ouml;ren.<\/p>\n<p>Damit &ouml;ffnet die Prozedur nun ein letztes Recordset, und zwar auf Basis der Tabelle mit den zu ersetzenden Feldwerten aus <b>rstTabellen<\/b>. Dieses Recordset wird in <b>rstAnonymisieren <\/b>abgelegt. Nun durchlaufen wie in einer <b>Do While<\/b>-Schleife alle Datens&auml;tze des Recordsets <b>rstAnonymisieren<\/b>, um die Feldinhalte entsprechend der Vorgaben des Benutzers zu &auml;ndern.<\/p>\n<p>Im Beispiel aus Bild 18 sollen etwa die beiden Felder <b>Vorname <\/b>und <b>Nachname <\/b>der Tabelle <b>tblPersonal <\/b>durch zuf&auml;llige Zeichenketten mit zwischen sieben und zehn Zeichen gef&uuml;llt werden. Der jeweils aktuelle Datensatz des Recordsets <b>qryAnonymisieren <\/b>wird dazu zun&auml;chst mit der Methode <b>Edit <\/b>in den Bearbeitungszustand versetzt.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2017_06\/pic_1112_018.png\" alt=\"Beispielkonfiguration\" width=\"599,593\" height=\"522,107\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 18: Beispielkonfiguration<\/span><\/b><\/p>\n<p>Dann durchlaufen wir alle im Recordset <b>rstFelder <\/b>gespeicherten Felder und pr&uuml;fen den Wert der Ersetzungsmethode (<b>ErsetzenMitID<\/b>). Ist dieser Wert <b>1<\/b>, was der einzigen aktuell vorliegenden Methode entspricht, dann ermittelt die Prozedur mit der Funktion <b>Zufallszeichenkette <\/b>den neuen Wert des Feldes und tr&auml;gt diesen auch direkt ein. Dies geschieht dann f&uuml;r alle Felder, die der Benutzer in der Konfiguration markiert hat. Dann wird der Datensatz mit der <b>Update<\/b>-Methode gespeichert und die &uuml;brigen Datens&auml;tze werden auf die gleiche Art und Weise behandelt.<\/p>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Dieser Beitrag hat ein kleines Framework vorgestellt, mit dem Sie Daten in Tabellen durch anonymisierte Daten ersetzen k&ouml;nnen.<\/p>\n<p>Bisher haben wir nur eine Funktion angelegt, mit der Textfelder mit zuf&auml;llig generierten Zeichenketten gef&uuml;llt werden k&ouml;nnen. Dies k&ouml;nnen Sie erweitern, indem Sie jeweils einen neuen Datensatz in der Tabelle <b>tbl-ErsetzenMit <\/b>anlegen, eine entsprechende Ersetzen-Funktion nach dem Beispiel der Funktion <b>Zufallszeichenkette <\/b>erstellen und die <b>Select Case<\/b>-Bedingung in der Prozedur <b>cmdAnonymisieren_Click <\/b>entsprechend erweitern.<\/p>\n<\/p>\n<h2>Downloads zu diesem Beitrag<\/h2>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>DatenAnonymisieren.accdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/75898328-DDCC-4B24-A1BF-2B1A558D81CD\/aiu_1112.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wenn ein potenzieller Kunde Sie um Unterst&uuml;tzung beim Programmieren oder Anpassen einer bestehenden Datenbank bittet, ist es am einfachsten, wenn diese Ihnen die Datenbank zum Analysieren zur Verf&uuml;gung stellt. Das scheitert aber oft daran, dass der Kunde die Datenbank nicht herausgeben darf, weil die enthaltenen Daten nicht weitergegeben werden d&uuml;rfen. Oft handelt es sich dabei um Adressdaten. Dieser Beitrag zeigt, wie Sie dem Kunden das Werkzeug bereitstellen, die enthaltenen Daten zu anonymisieren.<\/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":[662017,66062017,44000027],"tags":[],"class_list":["post-55001112","post","type-post","status-publish","format-standard","hentry","category-662017","category-66062017","category-Loesungen"],"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>Daten anonymisieren - 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\/Daten_anonymisieren\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Daten anonymisieren\" \/>\n<meta property=\"og:description\" content=\"Wenn ein potenzieller Kunde Sie um Unterst&uuml;tzung beim Programmieren oder Anpassen einer bestehenden Datenbank bittet, ist es am einfachsten, wenn diese Ihnen die Datenbank zum Analysieren zur Verf&uuml;gung stellt. Das scheitert aber oft daran, dass der Kunde die Datenbank nicht herausgeben darf, weil die enthaltenen Daten nicht weitergegeben werden d&uuml;rfen. Oft handelt es sich dabei um Adressdaten. Dieser Beitrag zeigt, wie Sie dem Kunden das Werkzeug bereitstellen, die enthaltenen Daten zu anonymisieren.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Daten_anonymisieren\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2022-03-30T19:04:49+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg09.met.vgwort.de\/na\/be02bba40d084c0c91fe4c4398cee7a2\" \/>\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\\\/Daten_anonymisieren\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Daten_anonymisieren\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Daten anonymisieren\",\"datePublished\":\"2022-03-30T19:04:49+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Daten_anonymisieren\\\/\"},\"wordCount\":5270,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Daten_anonymisieren\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/be02bba40d084c0c91fe4c4398cee7a2\",\"articleSection\":[\"2017\",\"6\\\/2017\",\"L\u00f6sungen\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Daten_anonymisieren\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Daten_anonymisieren\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Daten_anonymisieren\\\/\",\"name\":\"Daten anonymisieren - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Daten_anonymisieren\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Daten_anonymisieren\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/be02bba40d084c0c91fe4c4398cee7a2\",\"datePublished\":\"2022-03-30T19:04:49+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Daten_anonymisieren\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Daten_anonymisieren\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Daten_anonymisieren\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/be02bba40d084c0c91fe4c4398cee7a2\",\"contentUrl\":\"http:\\\/\\\/vg09.met.vgwort.de\\\/na\\\/be02bba40d084c0c91fe4c4398cee7a2\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Daten_anonymisieren\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Daten anonymisieren\"}]},{\"@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":"Daten anonymisieren - 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\/Daten_anonymisieren\/","og_locale":"de_DE","og_type":"article","og_title":"Daten anonymisieren","og_description":"Wenn ein potenzieller Kunde Sie um Unterst&uuml;tzung beim Programmieren oder Anpassen einer bestehenden Datenbank bittet, ist es am einfachsten, wenn diese Ihnen die Datenbank zum Analysieren zur Verf&uuml;gung stellt. Das scheitert aber oft daran, dass der Kunde die Datenbank nicht herausgeben darf, weil die enthaltenen Daten nicht weitergegeben werden d&uuml;rfen. Oft handelt es sich dabei um Adressdaten. Dieser Beitrag zeigt, wie Sie dem Kunden das Werkzeug bereitstellen, die enthaltenen Daten zu anonymisieren.","og_url":"https:\/\/access-im-unternehmen.de\/Daten_anonymisieren\/","og_site_name":"Access im Unternehmen","article_published_time":"2022-03-30T19:04:49+00:00","og_image":[{"url":"http:\/\/vg09.met.vgwort.de\/na\/be02bba40d084c0c91fe4c4398cee7a2","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\/Daten_anonymisieren\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Daten_anonymisieren\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Daten anonymisieren","datePublished":"2022-03-30T19:04:49+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Daten_anonymisieren\/"},"wordCount":5270,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Daten_anonymisieren\/#primaryimage"},"thumbnailUrl":"http:\/\/vg09.met.vgwort.de\/na\/be02bba40d084c0c91fe4c4398cee7a2","articleSection":["2017","6\/2017","L\u00f6sungen"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Daten_anonymisieren\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Daten_anonymisieren\/","url":"https:\/\/access-im-unternehmen.de\/Daten_anonymisieren\/","name":"Daten anonymisieren - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Daten_anonymisieren\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Daten_anonymisieren\/#primaryimage"},"thumbnailUrl":"http:\/\/vg09.met.vgwort.de\/na\/be02bba40d084c0c91fe4c4398cee7a2","datePublished":"2022-03-30T19:04:49+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Daten_anonymisieren\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Daten_anonymisieren\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/Daten_anonymisieren\/#primaryimage","url":"http:\/\/vg09.met.vgwort.de\/na\/be02bba40d084c0c91fe4c4398cee7a2","contentUrl":"http:\/\/vg09.met.vgwort.de\/na\/be02bba40d084c0c91fe4c4398cee7a2"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Daten_anonymisieren\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Daten anonymisieren"}]},{"@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\/55001112","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=55001112"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001112\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001112"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001112"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001112"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}