{"id":55001000,"date":"2015-10-01T00:00:00","date_gmt":"2020-05-22T19:11:20","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1000"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Kundendatensaetze_zusammenfuehren","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Kundendatensaetze_zusammenfuehren\/","title":{"rendered":"Kundendatens&auml;tze zusammenf&uuml;hren"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg07.met.vgwort.de\/na\/2a96ee5668104d51ae65fd83b14e229b\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>Wer eine Kundendatenbank pflegt, wird fr&uuml;her oder sp&auml;ter Dubletten in seiner Datenbank vorfinden. Sei es, weil Kunden sich mit neuer E-Mail und neuer Adresse erneut im Onlineshop anmelden und von dort importiert werden oder weil man bei der Suche nach einem vorhandenen Konto f&uuml;r einen Kunden wegen eines Tippfehlers keinen Treffer landet &#8211; langfristig lassen sich doppelte Kundendatens&auml;tze nicht verhindern. Aber das ist kein Problem: In Datenbanken l&auml;sst sich zum Gl&uuml;ck alles nachtr&auml;glich &auml;ndern. Wie dies bei Kundendaten und den damit verkn&uuml;pften Daten wie etwa Bestellungen funktioniert, erkl&auml;rt dieser Beitrag.<\/b><\/p>\n<p>Das Problem nach der Erstellen eines doppelten Kundendatensatzes lautet: Wie mache ich aus den beiden Kundendatens&auml;tzen einen, und vor allem: Wie sorge ich daf&uuml;r, dass die Daten, die mit dem zu l&ouml;schenden Kundendatensatz verkn&uuml;pft sind, mit dem verbleibenden Kundendatensatz zusammengef&uuml;hrt werden<\/p>\n<p>Es w&auml;re ja leicht, wenn man einfach den &#8222;alten&#8220; Kundendatensatz l&ouml;schen k&ouml;nnte und dann Ruhe h&auml;tte. Aber in der Regel erstellt man ja einen Kundendatensatz erst, wenn f&uuml;r diesen auch eine Bestellung vorliegt. Wenn wir ein Datenmodell wie in Bild 1 zugrunde legen, bei dem ein Bestelldatensatz auf der einen Seite mit dem bestellenden Kunden, auf der anderen Seite mit der Tabelle Bestelldetails verkn&uuml;pft wird, ist klar: Wir k&ouml;nnen nicht einfach den alten Datensatz l&ouml;schen, sondern m&uuml;ssen auch alle Daten der Tabelle <b>tblBestelldetails <\/b>auf den Kundendatensatz &uuml;bertragen, der beibehalten werden soll. Das bedeutet eigentlich nur, dass der Wert des Fremdschl&uuml;sselfeldes <b>KundeID<\/b> mit dem entsprechenden Prim&auml;rschl&uuml;sselwert des neuen Kundendatensatzes gef&uuml;llt werden muss. Das allein l&auml;sst sich mit einer einfachen <b>UPDATE<\/b>-Abfrage erledigen. Danach noch den nicht mehr ben&ouml;tigten Kundendatensatz l&ouml;schen oder als inaktiv markieren, schon ist man fertig.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2015_05\/pic_1000_001.png\" alt=\"Tabellen der Beispieldatenbank\" width=\"700\" height=\"369,4181\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Tabellen der Beispieldatenbank<\/span><\/b><\/p>\n<p>Um solche &auml;nderungen nachher wieder r&uuml;ckg&auml;ngig machen oder zumindest nachvollziehen zu k&ouml;nnen, sollten Sie die ge&auml;nderten oder gel&ouml;schten Daten archivieren. Wenn Sie Access ab Version 2010 verwenden, k&ouml;nnen Sie dies etwa gem&auml;&szlig; dem Beitrag <b>Ge&auml;nderte Daten archivieren <\/b>(<b>www.access-im-unternehmen.de\/925<\/b>) erledigen. Beim SQL Server w&uuml;rden Sie entsprechende Trigger nutzen oder in die gespeicherten Prozeduren, welche die Daten &auml;ndern, passende Anweisungen zum Sichern der Datens&auml;tze einbringen.<\/p>\n<h2>Benutzeroberfl&auml;che<\/h2>\n<p>Nun h&auml;tten wir die Theorie bereits erledigt. Wie aber sieht die Praxis aus Otto Normalverbraucher kann leider meist nicht mal eben eine <b>UPDATE<\/b>-Anweisung ins Direktfenster schmei&szlig;en, sodass wir f&uuml;r die gew&uuml;nschte Funktion eine entsprechende Benutzeroberfl&auml;che bereitstellen m&uuml;ssen. Nehmen wir doch ein herk&ouml;mmliches Formular mit Unterformular als Basis, wie es zur Darstellung von Daten aus zwei Tabellen einer 1:n-Beziehung aussieht. Im Beispiel aus Bild 2 finden Sie die Tabelle <b>tblKunden <\/b>im Hauptformular und die Bestellungen des aktuell angezeigten Kunden aus <b>tblBestellungen <\/b>im Unterformular vor.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2015_05\/pic_1000_002.png\" alt=\"Formular zur Anzeige der Bestellungen eines Kunden\" width=\"550\" height=\"416,8907\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Formular zur Anzeige der Bestellungen eines Kunden<\/span><\/b><\/p>\n<p>Im Gegensatz zum Standard-Bestellformular, das eine Bestellung samt Bestellpositionen anzeigt, fallen hier die Bestelldetails unter den Tisch. Wir k&ouml;nnen diese mit einem zweiten Unterformular nachreichen, das die Bestellpositionen zu der jeweils im ersten Unterformular ausgew&auml;hlten Bestellung anzeigt. Dies ist allerdings unn&ouml;tig, denn wir wollen ja nur die Bestellungen des nicht mehr ben&ouml;tigten Duplikats eines Kunden auf einen anderen Kunden &uuml;bertragen. Die Bestellpositionen sind ja ohnehin mit der Tabelle <b>tblBestellungen <\/b>verkn&uuml;pft und werden quasi &#8222;mit &uuml;bertragen&#8220;.<\/p>\n<p>Gibt es nun eine sinnvolle Variante, um diesen Datensatz mit einer Dublette zusammenzuf&uuml;hren Nun, eigentlich nicht &#8211; denn diese m&uuml;ssten wir ja auf jeden Fall erst einmal ausfindig machen. Gelegentlich wird sich vielleicht ein Kunde melden, der vielleicht Kundennewsletter an die beiden unterschiedlichen E-Mail-Adressen seiner beiden Accounts erh&auml;lt und somit das Vorhandensein eines Duplikats aufdecken. In der Regel sollten Sie sich allerdings, je nach der Anzahl der zu verwaltenden Kunden, von Zeit zu Zeit selbst auf die Suche nach Duplikaten machen.<\/p>\n<p>Aufmerksame und langj&auml;hrige <b>Access im Unternehmen<\/b>-Leser werden jetzt aufhorchen: War da nicht mal was Ja, genau: Im Beitrag <b>Duplikatsuche in Access <\/b>(<b>www.access-im-unternehmen.de\/744<\/b>) haben wir ein Formular vorgestellt, mit dem Sie flexibel Duplikate in Ihren Datenbest&auml;nden finden k&ouml;nnen. Die L&ouml;sung aus diesem Beitrag werden wir f&uuml;r unsere Zwecke nutzen und entsprechend aufbohren.<\/p>\n<h2>Integration der L&ouml;sung in eigene Datenbanken<\/h2>\n<p>Wenn Sie die L&ouml;sung in einer eigenen Datenbank nutzen m&ouml;chten, m&uuml;ssen Sie zun&auml;chst die folgenden Objekte aus der Beispieldatenbank in Ihre Datenbank importieren:<\/p>\n<ul>\n<li><b>frmDuplikatmanager<\/b><\/li>\n<li><b>sfmDuplikatfelder<\/b><\/li>\n<li><b>sfmFlex<\/b><\/li>\n<li><b>frmDuplikatdetails<\/b><\/li>\n<li><b>tblDuplikatfelder<\/b><\/li>\n<li><b>clsDatasheetForm<\/b><\/li>\n<li><b>clsDatasheetControl<\/b><\/li>\n<li><b>clsColumnWidths<\/b><\/li>\n<li><b>mdlTools<\/b><\/li>\n<\/ul>\n<p>Damit erhalten Sie schon einmal das Formular aus Bild 3 mit allen ben&ouml;tigten Unterformularen, Modulen und Klassen. Au&szlig;erdem f&uuml;gen Sie so eine Tabelle hinzu, welche die Konstellation f&uuml;r das Auffinden der Duplikate speichert.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2015_05\/pic_1000_003.png\" alt=\"Das Formular zum Ermitteln und Abgleichen der Duplikate einer Tabelle\" width=\"700\" height=\"430,5036\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Das Formular zum Ermitteln und Abgleichen der Duplikate einer Tabelle<\/span><\/b><\/p>\n<p>Au&szlig;erdem m&uuml;ssen Sie das Formular <b>frmDuplikatdetails <\/b>noch an die Gegebenheiten der Zieldatenbank anpassen. Das Formular sieht in der Beispieldatenbank im Entwurf wie in Bild 4 aus. Dieses Formular soll einen der zusammenzufassenden Datens&auml;tze mit den notwendigsten Informationen f&uuml;r den Abgleich darstellen. Das Hauptaugenmerk liegt dabei darauf, dass die verkn&uuml;pften Daten angezeigt werden, die beim Zusammenf&uuml;hren zweier (oder auch mehrerer) Duplikate ber&uuml;cksichtigt werden sollen. Auf diese Weise kann der Benutzer sich nochmals versichern, dass dort auch die richtigen Daten zusammengef&uuml;hrt werden.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2015_05\/pic_1000_004.png\" alt=\"Formular zur Anzeige der Details zu einem der Duplikate\" width=\"700\" height=\"314,3312\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Formular zur Anzeige der Details zu einem der Duplikate<\/span><\/b><\/p>\n<p>Das Formular soll mehrfach ge&ouml;ffnet werden. Mit einem Klick auf die Schaltfl&auml;che <b>Als Ziel &uuml;bernehmen <\/b>&uuml;bernimmt der Benutzer dann den entsprechenden Datensatz als Zieldatensatz f&uuml;r das Zusammenf&uuml;hren der Daten &#8211; die bis dahin ge&ouml;ffneten Detailformulare werden dann geschlossen.<\/p>\n<h2>Ablauf der Zusammenf&uuml;hrung zweier Datens&auml;tze<\/h2>\n<p>Das Formular <b>frmDuplikatmanager <\/b>bietet in einem Kombinationsfeld alle Tabellen der aktuellen Datenbank zur Auswahl an. Wenn der Benutzer eine Tabelle ausgew&auml;hlt hat, erscheinen alle Felder im linken, oberen Unterformular. Dort finden Sie neben der Spalte mit den Feldnamen noch zwei weitere Spalten &#8211; eine mit den f&uuml;r die Duplikatsuche zu verwendenden Felder und eine mit den Feldern, die im Ergebnis angezeigt werden sollen.<\/p>\n<p>Nachdem der Benutzer diese festgelegt hat (im Screen-shot sollen nur die E-Mail-Adressen abgeglichen werden und alle Felder in der Ergebnisliste erscheinen), klickt er auf die Schaltfl&auml;che <b>Duplikate suchen<\/b>. Findet die L&ouml;sung mindestens ein Duplikat, zeigt es die Anzahl der gefundenen Exemplare sowie den Wert des Vergleichsfeldes im Unterformular rechts oben an.<\/p>\n<p>Hier kann der Benutzer nun wiederum auf einen Eintrag klicken und so alle Duplikate zu diesem Eintrag im unteren Unterformular einblenden. Dies hat den Vorteil, dass Sie direkt pr&uuml;fen k&ouml;nnen, ob sich die Inhalte der &uuml;brigen Felder unterscheiden. Wenn Sie sich entschieden haben, welcher der Datens&auml;tze beibehalten werden soll, k&ouml;nnen Sie in diesem gegebenenfalls Korrekturen vornehmen oder Informationen aus den zu l&ouml;schenden Datens&auml;tzen &uuml;bernehmen.<\/p>\n<p>Nun kommt auch unser Formular f&uuml;r die Anzeige der Duplikatdetails ins Spiel. Der Hauptgrund f&uuml;r die Erstellung der vorliegenden L&ouml;sung ist ja, nicht nur einen von mehreren Datens&auml;tzen (im Beispiel Kunden) zu &uuml;bernehmen und die &uuml;brigen zu l&ouml;schen, sondern auch noch die Daten, die mit den zu l&ouml;schenden Datens&auml;tzen verkn&uuml;pft sind, auf den verbleibenden Datensatz zu &uuml;bertragen.<\/p>\n<p>Deshalb k&ouml;nnen Sie mit dem Formular <b>frmDuplikatdetails<\/b> in diesem Beispiel die Kundendaten plus die Bestellungen der Kunden anzeigen, und zwar per Doppelklick auf einen der Eintr&auml;ge im unteren Unterformular. Im Gegensatz zu &uuml;blichen Formularen, von denen Sie nur jeweils eine einzige Instanz &ouml;ffnen, k&ouml;nnen Sie hier f&uuml;r jeden der in der Liste enthaltenen Kunden ein Detailformular &ouml;ffnen. In Bild 5 sehen Sie beispielsweise zwei Formulare mit verschiedenen Datens&auml;tzen zum gleichen Kunden. Klicken Sie hier auf die Schaltfl&auml;che <b>Als Ziel &uuml;bernehmen<\/b>, werden alle Formulare geschlossen. Au&szlig;erdem markiert das Formular <b>frmDuplikatmanager <\/b>den zu &uuml;bernehmenden Datensatz im unteren Unterformular.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2015_05\/pic_1000_005.png\" alt=\"Vergleich zweier Datens&auml;tze f&uuml;r den gleichen Kunden\" width=\"700\" height=\"293,1206\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Vergleich zweier Datens&auml;tze f&uuml;r den gleichen Kunden<\/span><\/b><\/p>\n<p>Nun folgt der interessante Teil: Das untere Listenfeld des Formulars <b>frmDuplikatmanager<\/b> zeigt alle mit der zusammenzuf&uuml;hrenden Tabelle per 1:n-Beziehung verkn&uuml;pften Tabellen an, in diesem Fall <b>tblBestellungen <\/b>und <b>tblNotizen<\/b> (s. Bild 6).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2015_05\/pic_1000_006.png\" alt=\"Auswahl der zu &uuml;bernehmenden Daten aus den verkn&uuml;pften Tabellen\" width=\"650\" height=\"369,7496\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Auswahl der zu &uuml;bernehmenden Daten aus den verkn&uuml;pften Tabellen<\/span><\/b><\/p>\n<p>Sie k&ouml;nnen nun einen oder mehrere Eintr&auml;ge ausw&auml;hlen, damit die enthaltenen Datens&auml;tze auf den zu &uuml;bernehmenden Kundendatensatz &uuml;bertragen werden. Um die Duplikate letztlich zusammenzuf&uuml;hren, klicken Sie auf die Schaltfl&auml;che rechts neben dem Listenfeld.<\/p>\n<p>Danach sollte der &uuml;bernommene Datensatz rasch vom unteren Unterformular verschwinden. &uuml;ber einen Doppelklick auf den verbleibenden Datensatz k&ouml;nnen Sie sich im Detailformular vergewissern, dass die verkn&uuml;pften Daten wie gew&uuml;nscht &uuml;bernommen wurden.<\/p>\n<h2>Sicher ist sicher<\/h2>\n<p>Zur Sicherheit sollten Sie solche Aktionen nicht durchf&uuml;hren, ohne zuvor eine Kopie der Datenbank angelegt zu haben. Noch besser w&auml;re es, wenn Sie Access 2010 oder h&ouml;her verwenden und die ge&auml;nderten oder gel&ouml;schten Datens&auml;tze in entsprechenden Archivtabellen sichern. Eine geeignete L&ouml;sung finden Sie in den Beitr&auml;gen <b>Ge&auml;nderte Daten archivieren <\/b>auf (<b>www.access-im-unternehmen.de\/925<\/b>) und <b>&auml;nderungshistorie implantieren <\/b>(<b>www.access-im-unternehmen.de\/995<\/b>).<\/p>\n<h2>Aufbau der ben&ouml;tigten Formulare<\/h2>\n<p>Die folgenden Abschnitte erl&auml;utern die Zusammenh&auml;nge zwischen dem Haupt- und den Unterformularen und wie diese gef&uuml;llt werden und auf Benutzeraktionen reagieren.<\/p>\n<h2>Laden des Formulars<\/h2>\n<p>Beim Laden des Formulars <b>frmDuplikatmanager<\/b> m&uuml;ssen einige Aktionen ausgef&uuml;hrt werden, um das Formular vorzubereiten. Dies geschieht in der Ereignisprozedur, die durch das Ereignis <b>Beim Laden <\/b>ausgel&ouml;st wird (s. Listing 1).<\/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;\">Set<\/span> db = CurrentDb\r\n     db.Execute \"DELETE FROM tblDuplikatfelder\", dbFailOnError\r\n     Me!sfmDuplikatfelder.Form.Requery\r\n     <span style=\"color:blue;\">Set<\/span> frm_sfmDuplikate = Me!sfmDuplikate.Form\r\n     frm_sfmDuplikate.OnCurrent = \"[Event Procedure]\"\r\n     <span style=\"color:blue;\">Set<\/span> objCW_Duplikatfelder = <span style=\"color:blue;\">New<\/span> clsColumnWidths\r\n     <span style=\"color:blue;\">Set<\/span> objCW_Duplikatfelder.DataSheetForm = Me!sfmDuplikatfelder.Form\r\n     <span style=\"color:blue;\">Set<\/span> objCW_Duplikate = <span style=\"color:blue;\">New<\/span> clsColumnWidths\r\n     <span style=\"color:blue;\">Set<\/span> objCW_Duplikate.DataSheetForm = Me!sfmDuplikate.Form\r\n     <span style=\"color:blue;\">Set<\/span> objCW_DuplikateDetail = <span style=\"color:blue;\">New<\/span> clsColumnWidths\r\n     <span style=\"color:blue;\">Set<\/span> objCW_DuplikateDetail.DataSheetForm = Me!sfmDuplikateDetails.Form\r\n     DatasheetFormInstanzieren\r\n     <span style=\"color:blue;\">Set<\/span> colForms = <span style=\"color:blue;\">New<\/span> Collection\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Vorbereitung des Formulars und seiner Elemente<\/span><\/b><\/p>\n<p>Diese leert zun&auml;chst die Tabelle <b>tblDuplikatfelder<\/b>, welche sp&auml;ter mit je einem Datensatz f&uuml;r jedes Feld der zu untersuchenden Tabelle gef&uuml;llt wird (s. Bild 7). Danach aktualisiert sie den Inhalt des Unterformulars <b>sfmDuplikatfelder<\/b>, damit dieses den aktualisierten Inhalt der nun leeren Tabelle <b>tblDuplikatfelder <\/b>anzeigt. Anschlie&szlig;end f&uuml;llt sie die folgende Variable mit einem Verweis auf das Unterformular <b>sfmDuplikate<\/b>:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2015_05\/pic_1000_007.png\" alt=\"Tabelle zum Speichern der zu verwendenden Felder\" width=\"425\" height=\"362,3963\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 7: Tabelle zum Speichern der zu verwendenden Felder<\/span><\/b><\/p>\n<pre><span style=\"color:blue;\">Dim <\/span>WithEvents frm_sfmDuplikate<span style=\"color:blue;\"> As <\/span>Form<\/pre>\n<p>Dies geschieht mit dem Schl&uuml;sselwort <b>WithEvents<\/b>, weil wir im Klassenmodul des Hauptformulars Ereignisse f&uuml;r dieses Unterformular implementieren wollen &#8211; zum Beispiel f&uuml;r die Auswahl eines der enthaltenen Datens&auml;tze, um dann alle passenden Duplikate im Unterformular <b>sfmDuplikatdetails <\/b>anzuzeigen. Dabei handelt es sich um das Ereignis <b>Beim Anzeigen<\/b>, wozu wir noch mitteilen m&uuml;ssen, dass das aktuelle Klassenmodul auf solche Ereignisse lauschen soll (<b>OnCurrent = [Event Procedure]<\/b>).<\/p>\n<p>Die drei Unterformulare <b>sfmDuplikatfelder<\/b>, <b>sfmDuplikate<\/b> und <b>sfmDuplikateDetails <\/b>sollen mit optimierter Spaltenbreite angezeigt werden. Dazu verwenden wir die Klasse <b>clsColumnWidths<\/b>, die wir ausf&uuml;hrlich im Beitrag <b>Spaltenbreiten optimieren mit Klasse vorstellen <\/b>(siehe <b>www.access-im-unternehmen.de\/998<\/b>).<\/p>\n<p>Wir wollen jedes der drei Unterformulare mit der Funktion zur optimalen Anpassung der Spaltenbreiten ausstatten, also legen wir drei Objektvariablen f&uuml;r die entsprechenden Objekte fest:<\/p>\n<pre><span style=\"color:blue;\">Dim <\/span>objCW_Duplikatfelder<span style=\"color:blue;\"> As <\/span>clsColumnWidths\r\n<span style=\"color:blue;\">Dim <\/span>objCW_Duplikate<span style=\"color:blue;\"> As <\/span>clsColumnWidths\r\n<span style=\"color:blue;\">Dim <\/span>objCW_DuplikateDetail<span style=\"color:blue;\"> As <\/span>clsColumnWidths<\/pre>\n<p><!--30percent--><\/p>\n<p>Diese instanziert die Prozedur <b>Form_Load <\/b>dann und stellt mit der Eigenschaft <b>DataSheetForm <\/b>jeweils das betroffene Unterformular ein.<\/p>\n<p>Danach ruft sie noch die Prozedur <b>DatasheetFormInstanzieren <\/b>auf, die dem unteren Unterformular einige Funktionen hinzuf&uuml;gt, und instanziert ein <b>Collection<\/b>-Objekt, das wir im Kopf des Klassenmoduls deklarieren:<\/p>\n<pre><span style=\"color:blue;\">Dim <\/span>colForms<span style=\"color:blue;\"> As <\/span>Collection<\/pre>\n<p>Den Zweck dieser Collection erl&auml;utern wir weiter unten.<\/p>\n<h2>Instanzieren der Datenblattfunktionen von sfmDuplikateDetails<\/h2>\n<p>Das untere Unterformular namens <b>sfmDuplikateDetails <\/b>soll bei einem Doppelklick auf einen der Datens&auml;tze ein Ereignis ausl&ouml;sen, um einen Detaildatensatz in einem eigenen Formular anzuzeigen. Damit der Benutzer dabei nur auf eine beliebige Stelle im Datensatz zu klicken braucht, m&uuml;ssen wir theoretisch f&uuml;r jedes Steuerelement eine <b>Beim Klicken<\/b>-Ereignisprozedur anlegen.<\/p>\n<p>Dies k&ouml;nnen wir uns jedoch sparen, wenn wir die beiden im Beitrag <b>Datenblattereignisse mit Klasse <\/b>vorgestellten Klassen nutzen. Damit brauchen wir als Erstes nur ein Element mit folgendem Typ zu deklarieren:<\/p>\n<pre><span style=\"color:blue;\">Dim <\/span>WithEvents objDS<span style=\"color:blue;\"> As <\/span>clsDataSheetForm<\/pre>\n<p>Danach ben&ouml;tigen wir noch die Anweisungen aus der Routine aus Listing 2. Diese instanziert das Objekt auf Basis von <b>clsDatasheetForm <\/b>und weist diesem das Unterformular <b>sfmDuplikateDetails <\/b>als Formular zu. Au&szlig;erdem stellt sie die Eigenschaft <b>PrimaryKey <\/b>auf den Prim&auml;rschl&uuml;sselnamen der zu untersuchenden Tabelle ein, den wir mit der Funktion <b>GetSinglePrimaryKey <\/b>einlesen (siehe <b>Prim&auml;rschl&uuml;sselfelder ermitteln<\/b>, <b>www.access-im-unternehmen.de\/1004<\/b>. Mit <b>ZeileBeiKlickMarkieren = False <\/b>legen wir fest, dass beim Anklicken nicht die komplette Zeile des Datensatzes markiert werden soll.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>DatasheetFormInstanzieren()\r\n     <span style=\"color:blue;\">Set<\/span> objDS = <span style=\"color:blue;\">New<\/span> clsDatasheetForm\r\n     <span style=\"color:blue;\">With<\/span> objDS\r\n         <span style=\"color:blue;\">Set<\/span> .DatasheetForm = Me!sfmDuplikateDetails.Form\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(Me!cboTabellen) &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n             .PrimaryKey = GetSinglePrimaryKey(Nz(Me!cboTabellen))\r\n             .ZeileBeiKlickMarkieren = <span style=\"color:blue;\">False<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     End <span style=\"color:blue;\">With<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Funktionen f&uuml;r das Unterformular sfmDuplikateDetails einrichten<\/span><\/b><\/p>\n<h2>Auswahl der Tabelle<\/h2>\n<p>Das Hauptformular <b>frmDuplikatmanager <\/b>verwendet das Kombinationsfeld <b>cboTabellen<\/b>, um dem Benutzer die Auswahl der zu untersuchenden Tabelle zu erm&ouml;glichen. Diese f&uuml;llen wir mit der folgenden Abfrage:<\/p>\n<pre>SELECT MSysObjects.Name FROM MSysObjects WHERE Name <span style=\"color:blue;\">Not<\/span> Like ''MSys*'' And Name <span style=\"color:blue;\">Not<\/span> Like ''USys*'' And Type=1<\/pre>\n<p>Nach dem Ausw&auml;hlen eines der Eintr&auml;ge l&ouml;st das Ereignis <b>Nach Aktualisierung <\/b>des Kombinationsfeldes die Prozedur aus Listing 3 aus. Die Prozedur leert die Tabelle <b>tblDuplikatfelder<\/b>, welche die Konfiguration f&uuml;r die Duplikatsuche speichert, und f&uuml;llt diese dann neu, indem sie in einer <b>For Each<\/b>-Schleife alle Felder der zu untersuchenden Tabelle durchl&auml;uft und f&uuml;r jedes einen neuen Datensatz zur Tabelle <b>tblDuplikatfelder <\/b>hinzuf&uuml;gt. Dann aktualisiert sie das Unterformular <b>sfmDuplikatfelder <\/b>und optimiert mit der Methode <b>OptimizeColumnWidths <\/b>die Spaltenbreiten.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cboTabellen_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>fld<span style=\"color:blue;\"> As <\/span>DAO.Field\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>strVerknuepfteTabellen()<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strFremdschluesselfelder()<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strRowSource<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>i<span style=\"color:blue;\"> As Integer<\/span>, intAnzahlVerknuepfungen<span style=\"color:blue;\"> As Integer<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> tdf = db.TableDefs(Me!cboTabellen)\r\n     db.Execute \"DELETE FROM tblDuplikatfelder\", dbFailOnError\r\n     For Each fld In tdf.Fields\r\n         db.Execute \"INSERT INTO tblDuplikatfelder(Feldname, FeldAnzeigen) VALUES(''\" & fld.Name & \"'', <span style=\"color:blue;\">True<\/span>)\", _\r\n             dbFailOnError\r\n     <span style=\"color:blue;\">Next<\/span> fld\r\n     Me!sfmDuplikatfelder.Form.Requery\r\n     objCW_Duplikatfelder.OptimizeColumnWidths\r\n     intAnzahlVerknuepfungen = VerknuepfteTabellen(Me!cboTabellen, strVerknuepfteTabellen(), strFremdschluesselfelder())\r\n     Me!lstVerknuepfteTabellen.RowSourceType = \"Value List\"\r\n     For i = 0 To intAnzahlVerknuepfungen - 1\r\n         strRowSource = strRowSource & strVerknuepfteTabellen(i) & \";\" & strFremdschluesselfelder(i) & \";\"\r\n     <span style=\"color:blue;\">Next<\/span> i\r\n     Me!lstVerknuepfteTabellen.RowSource = strRowSource\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Aktionen nach der Auswahl einer Tabelle<\/span><\/b><\/p>\n<p>Dann ruft sie eine Funktion namens <b>VerknuepfteTabellen <\/b>auf, die alle per 1:n-Beziehung verkn&uuml;pften Tabellen sowie die Namen der Fremdschl&uuml;sselfelder liefert (siehe <b>Verkn&uuml;pfte Tabellen ermitteln<\/b>, <b>www.access-im-unternehmen.de\/1005<\/b>). Sie erwartet den Namen der Tabelle und liefert zwei Arrays mit den Ergebnissen zur&uuml;ck &#8211; sowie als Funktionswert die Anzahl der Ergebnisse. Dies durchl&auml;uft die folgende <b>For&#8230;Next<\/b>-Schleife und stellt dabei die Datensatzherkunft f&uuml;r das Listenfeld <b>lstVerknuepfteTabellen <\/b>im Fu&szlig; des Formulars zusammen (zum Beispiel <b>tblBestellungen;tblNotizen;<\/b>).<\/p>\n<h2>Duplikatfelder ausw&auml;hlen<\/h2>\n<p>Der n&auml;chste Schritt ist die Auswahl der als Duplikatfelder zu nutzenden Felder im Unterformular <b>sfmDuplikatfelder <\/b>und das anschlie&szlig;ende Bet&auml;tigen der Schaltfl&auml;che <b>cmdDuplikateSuchen<\/b>. Dies l&ouml;st die Prozedur aus Listing 4 aus. Die Prozedur liest ein Recordset mit allen Datens&auml;tzen der soeben frisch gef&uuml;llten Tabelle <b>tblDuplikatfelder <\/b>ein, die als Vergleichsfelder ausgew&auml;hlt wurden (<b>Duplikatfeld = True<\/b>). Sie durchl&auml;uft diese Tabelle und stellt eine kommaseparierte Liste der Feldnamen zusammen, also etwa <b>EMail, Nachname, PLZ,<\/b>, und entfernt anschlie&szlig;end das abschlie&szlig;ende Komma. Das Feld aus dem ersten Datensatz speichert die Prozedur in der Variablen <b>strAnzahlfeld<\/b>. Damit stellt die Prozedur eine Abfrage zusammen, welche die Anzahl der gefundenen Duplikate enth&auml;lt &#8211; hier f&uuml;r eine Duplikatsuche nur nach dem Feld <b>EMail<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdDuplikateSuchen_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>qdf<span style=\"color:blue;\"> As <\/span>DAO.QueryDef\r\n     <span style=\"color:blue;\">Dim <\/span>strSQL<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strDuplikatfelder<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strAnzahlfeld<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>rstDuplikatfelder<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> rstDuplikatfelder = _\r\n         db.OpenRecordset(\"SELECT * FROM tblDuplikatfelder WHERE Duplikatfeld = True\", dbOpenDynaset)\r\n     <span style=\"color:blue;\">If <\/span>rstDuplikatfelder.EOF<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Bitte w&auml;hlen Sie mindestens ein Feld aus, das als Kriterium f&uuml;r die Duplikatsuche dienen soll.\"\r\n         <span style=\"color:blue;\">Exit Sub<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     strAnzahlfeld = rstDuplikatfelder!Feldname\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstDuplikatfelder.EOF\r\n         strDuplikatfelder = strDuplikatfelder & rstDuplikatfelder!Feldname & \",\"\r\n         rstDuplikatfelder.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     strDuplikatfelder = <span style=\"color:blue;\">Left<\/span>(strDuplikatfelder, <span style=\"color:blue;\">Len<\/span>(strDuplikatfelder) - 1)\r\n     strSQL = \"SELECT Count(\" & strAnzahlfeld & \") AS Duplikatanzahl, \"\r\n     strSQL = strSQL & strDuplikatfelder & \" FROM \" & Me!cboTabellen\r\n     strSQL = strSQL & \" GROUP BY \" & strDuplikatfelder & \" HAVING Count(\" & strAnzahlfeld & \") &gt; 1\"\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> IsNull(Me!cboTabellen)<span style=\"color:blue;\"> Then<\/span>\r\n         On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n         db.QueryDefs.Delete \"_qryDuplikate\"\r\n         <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n         db.QueryDefs.Refresh\r\n         <span style=\"color:blue;\">Set<\/span> qdf = db.CreateQueryDef(\"_qryDuplikate\", strSQL)\r\n         Me!sfmDuplikate.Form.FillFlexForm \"_qryDuplikate\"\r\n         Me!sfmDuplikate.Form.Requery\r\n         objCW_Duplikate.OptimizeColumnWidths\r\n         DatasheetFormInstanzieren\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: Suchen der Duplikate<\/span><\/b><\/p>\n<pre>SELECT Count(EMail) AS Duplikatanzahl, EMail \r\nFROM tblKunden GROUP BY EMail HAVING Count(EMail) &gt; 1<\/pre>\n<p>Die Abfrage gruppiert die Datens&auml;tze der Tabelle <b>tblKunden<\/b> nach dem Feld <b>EMail <\/b>und z&auml;hlt die Anzahl der jeweils gefundenen Datens&auml;tze. Diejenigen, deren Anzahl gr&ouml;&szlig;er als <b>1 <\/b>ist, sind logischerweise Duplikate und werden von der Abfrage zur&uuml;ckgeliefert.<\/p>\n<p>Mit diesem SQL-Ausdruck im Gep&auml;ck l&ouml;scht die Prozedur eine eventuell noch vorhandene Abfrage namens <b>_qryDuplikate <\/b>und erstellt diese neu.<\/p>\n<p>Dann ruft sie die Methode <b>FillFlexForm <\/b>des Unterformulars <b>sfmDuplikate <\/b>auf und &uuml;bergibt dieser den Namen der soeben erstellten Abfrage <b>_qryDuplikate<\/b>. Hier h&auml;ngt noch ein kleiner Rattenschwanz an Aktionen dran: Das Aktualisieren des Unterformulars zieht n&auml;mlich noch das Ausl&ouml;sen des Ereignisses <b>Beim Anzeigen <\/b>des Unterformulars nach sich, das wir im Hauptformular implementiert haben. Mehr dazu weiter unten.<\/p>\n<p>Nachdem dies geschehen ist, aktualisiert die Prozedur die Datenherkunft des Formulars und ruft die Methode <b>OptimizeColumnWidths <\/b>auf, um die Spaltenbreiten f&uuml;r die nun angezeigten Daten zu optimieren.<\/p>\n<p>Mehr &uuml;ber die Methode <b>FillFlexForm <\/b>lesen Sie im Beitrag <b>Flexibles Unterformular <\/b>(<b>http:\/\/www.access-im-unternehmen.de\/743<\/b>).<\/p>\n<p>Es handelt sich bei dem Unterformular <b>sfmDuplikate <\/b>um ein Formular, das eine Anzahl vorgefertigter Textfelder, Kombinationsfelder und Kontrollk&auml;stchen enth&auml;lt, die dann je nach der Gestalt der anzuzeigenden Datenherkunft eingeblendet werden.<\/p>\n<p>Nach dem F&uuml;llen des Formulars ruft die Prozedur noch die Methode <b>DatasheetFormInstanzieren<\/b> auf, welche das neu gef&uuml;llte Unterformular <b>sfmDuplikateDetails <\/b>mit den Funktionen zum Weiterleiten von Mausklicks an das Hauptformular ausstattet (siehe oben).<\/p>\n<h2>Anzeige der Duplikate im Unterformular<\/h2>\n<p>Das Unterformular <b>sfmDuplikate <\/b>zeigt die Basisdaten der gefundenen Duplikate samt der Anzahl der Exemplare an. Wenn der Benutzer auf einen der Eintr&auml;ge klickt, soll das untere Unterformular <b>sfmDuplikateDetails <\/b>die jeweiligen Datens&auml;tze der Tabelle anzeigen (s. Bild 8).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2015_05\/pic_1000_008.png\" alt=\"Aktualisieren von einem Unterformular zum n&auml;chsten\" width=\"700\" height=\"383,6854\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 8: Aktualisieren von einem Unterformular zum n&auml;chsten<\/span><\/b><\/p>\n<p>Die dazu notwendige Ereignisprozedur wollen wir auch im Klassenmodul des Hauptformulars abbilden und nicht im Unterformular. Dazu haben wir ja die Objektvariable <b>frm_sfmDuplikate <\/b>deklariert und im Ereignis <b>Form_Load <\/b>eingestellt, dass wir das Ereignis <b>OnCurrent <\/b>im Klassenmodul des Hauptformulars implementieren wollen.<\/p>\n<p>Die passende Ereignisprozedur legen Sie an, indem Sie im Klassenmodul des Hauptformulars im linken Kombinationsfeld den Eintrag <b>frm_sfmDuplikate <\/b>und im rechten den Eintrag <b>OnCurrent <\/b>ausw&auml;hlen. Die dann automatisch erstellte Prozedur erg&auml;nzen Sie wie in Listing 5.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>frm_sfmDuplikate_Current()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database, qdf<span style=\"color:blue;\"> As <\/span>DAO.QueryDef\r\n     <span style=\"color:blue;\">Dim <\/span>strSQL<span style=\"color:blue;\"> As String<\/span>, strAnzeigefelder<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>rstDuplikatfelder<span style=\"color:blue;\"> As <\/span>DAO.Recordset, rstAnzeigefelder<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>strWhere<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> rstAnzeigefelder = db.OpenRecordset( _\r\n         \"SELECT Feldname FROM tblDuplikatfelder WHERE FeldAnzeigen = True\", dbOpenDynaset)\r\n     <span style=\"color:blue;\">If <\/span>rstAnzeigefelder.EOF<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Bitte w&auml;hlen Sie mindestens ein Feld aus, das in der Detailliste der Duplikate erscheinen soll.\"\r\n         <span style=\"color:blue;\">Exit Sub<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstAnzeigefelder.EOF\r\n         strAnzeigefelder = strAnzeigefelder & rstAnzeigefelder!Feldname & \",\"\r\n         rstAnzeigefelder.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     strAnzeigefelder = <span style=\"color:blue;\">Left<\/span>(strAnzeigefelder, <span style=\"color:blue;\">Len<\/span>(strAnzeigefelder) - 1)\r\n     <span style=\"color:blue;\">Set<\/span> rstDuplikatfelder = db.OpenRecordset(\"SELECT Feldname FROM tblDuplikatfelder WHERE Duplikatfeld = True\", _\r\n         dbOpenDynaset)\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstDuplikatfelder.EOF\r\n         On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n         strWhere = strWhere & rstDuplikatfelder!Feldname & \"=''\" _\r\n             & Me!sfmDuplikate. Form.Recordset.Fields(rstDuplikatfelder!Feldname) & \"'' AND \"\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> Err.Number = 0<span style=\"color:blue;\"> Then<\/span>\r\n             <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n             <span style=\"color:blue;\">Exit Sub<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n         rstDuplikatfelder.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strWhere) &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n         strWhere = <span style=\"color:blue;\">Left<\/span>(strWhere, <span style=\"color:blue;\">Len<\/span>(strWhere) - 4)\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n     db.QueryDefs.Delete \"_qryDuplikateDetail\"\r\n     <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n     db.QueryDefs.Refresh\r\n     strSQL = \"SELECT \" & strAnzeigefelder & \" FROM \" & Me!cboTabellen & \" WHERE \" & strWhere\r\n     <span style=\"color:blue;\">Set<\/span> qdf = db.CreateQueryDef(\"_qryDuplikateDetail\", strSQL)\r\n     Me!sfmDuplikateDetails.Form.FillFlexForm \"_qryDuplikateDetail\"\r\n     Me!sfmDuplikateDetails.Form.Requery\r\n     <span style=\"color:blue;\">Set<\/span> objCW_DuplikateDetail.DataSheetForm = Me!sfmDuplikateDetails.Form\r\n     objCW_DuplikateDetail.OptimizeColumnWidths\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 5: Aktualisieren des Unterformulars zur Anzeige der Duplikate<\/span><\/b><\/p>\n<p>Die Prozedur erstellt ein Recordset mit den anzuzeigenden Feldern aus der Tabelle <b>tblDuplikatfelder <\/b>(<b>FeldAnzeigen = True<\/b>). Sie durchl&auml;uft dann alle Felder in einer <b>Do While<\/b>-Schleife und stellt eine kommaseparierte Liste der Felder zusammen (<b>KundeID, Firma, Vorname, Nachname,<\/b>), von der sie wieder das letzte Komma entfernt.<\/p>\n<p>Ein zweites Recordset nimmt alle Felder der Tabelle <b>tblDuplikatfelder <\/b>auf, die f&uuml;r den Abgleich der Duplikate markiert wurden (<b>Duplikatfeld = True<\/b>). Auch dieses Recordset durchl&auml;uft die Prozedur in einer <b>Do While<\/b>-Schleife. Dabei stellt sie eine Reihe von Bedingungen zusammen, die durch den <b>AND<\/b>-Operator getrennt werden. Der Feldname ist der erste Teil der Bedingung, der Wert des Duplikatfeldes des aktuell markierten Datensatzes im Unterformular <b>sfmDuplikate <\/b>ist der Vergleichswert. Heraus kommt so etwas: <b>EMail = &#8220;andre@minhorst.com&#8220; AND Vorname = &#8220;Andr&eacute;&#8220; AND<\/b>, wobei das abschlie&szlig;ende <b>AND <\/b>wieder abgeschnitten wird.<\/p>\n<p>Dann f&uuml;gt die Prozedur die beiden Listen mit ein paar SQL-Schl&uuml;sselw&ouml;rtern zusammen, sodass eine Abfrage wie die folgende herauskommt:<\/p>\n<pre>SELECT KundeID, Firma, Vorname, Nachname \r\nFROM tblKunden \r\nWHERE EMail = ''andre@minhorst.com'' AND Vorname = ''Andr&eacute;''<\/pre>\n<p>Damit erstellt die Prozedur ein neues Abfrageobjekt namens <b>_qryDuplikateDetail<\/b> und f&uuml;llt das Unterformular <b>sfmDuplikateDetails <\/b>&uuml;ber die Methode <b>FillFlexForm <\/b>mit den Datens&auml;tzen der Abfrage. Nach dem Aktualisieren der Datenherkunft des Unterformulars weist die Prozedur dem Objekt <b>objCW_DuplikateDetail <\/b>das Unterformular zu und optimiert mit dessen Methode <b>OptimizeColumnWidths <\/b>die Spaltenbreiten.<\/p>\n<p>Das Ergebnis ist die Anzeige der doppelten Datens&auml;tze im Unterformular <b>sfmDuplikateDetails<\/b>.<\/p>\n<h2>Details zu einem Datensatz anzeigen<\/h2>\n<p>Die Klasse <b>objDS<\/b> soll uns ja erm&ouml;glichen, Ereignisse, die durch einen Klick oder Doppelklick auf einen der Datens&auml;tze des Unterformulars <b>sfmDuplikateDetails<\/b> ausgel&ouml;st werden, im Hauptformular auszuwerten. Dies erledigen wir mit einer Ereignisprozedur, die durch das Ereignis <b>DblClick <\/b>des Objekts <b>objDS <\/b>ausgel&ouml;st wird.<\/p>\n<p>Diese Anzeige hat die Eigenart, dass wir das gleiche Formular gleich mehrfach instanzieren, um mehrere Datens&auml;tze gleichzeitig anzeigen zu k&ouml;nnen. Aus technischen Gr&uuml;nden ben&ouml;tigen wir eine Objektvariable namens <b>frmDuplikatdetails<\/b>. Damit k&ouml;nnen wir die ge&ouml;ffneten Formulare sp&auml;ter steuern:<\/p>\n<pre><span style=\"color:blue;\">Dim <\/span>frmDuplikatdetails<span style=\"color:blue;\"> As <\/span>Form_frmDuplikatdetails<\/pre>\n<p>Die Prozedur liefert einen Verweis auf das im Unterformular angeklickte Steuerelement sowie den Prim&auml;rschl&uuml;sselwert des Datensatzes als Parameter. Wir speichern den Prim&auml;rschl&uuml;sselwert in der Variablen <b>lngID <\/b>und &ouml;ffnen dann das Detailformular. Dies geschieht nicht wie sonst mit der <b>DoCmd.OpenForm<\/b>-Methode, sondern durch Instanzieren der Formularklasse mit dem <b>New<\/b>-Schl&uuml;sselwort. Das Ergebnis referenzieren wir dann mit der Variablen <b>frmDuplikatdetails<\/b>. Dieses filtern wir dann nach dem Prim&auml;rschl&uuml;sselwert. Den Filterausdruck setzen wir mithilfe der Funktion <b>GetSinglePrimaryKey<\/b>, die den Namen des Prim&auml;rschl&uuml;sselfeldes liefert, und dem Wert aus <b>lngID <\/b>zusammen (<b>KundeID = 123<\/b>).<\/p>\n<p>Diesen Ausdruck weisen wir der Eigenschaft <b>Filter<\/b> zu und aktivieren den Filter durch Einstellen von <b>FilternOn <\/b>auf den Wert <b>True<\/b>. Schlie&szlig;lich speichern wir den Wert des Prim&auml;rschl&uuml;sselfeldes in der <b>Tag<\/b>-Eigenschaft des Formulars und blenden dieses mit <b>Visible = True <\/b>ein.<\/p>\n<p>Da wir auf diese Weise nicht nur eines, sondern mehrere Formulare &ouml;ffnen wollen, m&uuml;ssen wir die Referenz auf das Formular aus der Variablen <b>frmDuplikatdetails <\/b>noch sichern. Dazu nutzen wir die Collection <b>colForms <\/b>und weisen dieser den Verweis auf das Formular sowie den Wert des Prim&auml;rschl&uuml;sselfeldes als <b>Key<\/b> zu:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>objDS_DblClick(ctl<span style=\"color:blue;\"> As <\/span>Control, _\r\n         varPKValue<span style=\"color:blue;\"> As Variant<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>lngID<span style=\"color:blue;\"> As Long<\/span>\r\n     lngID = objDS.PrimaryKeyValue\r\n     <span style=\"color:blue;\">Set<\/span> frmDuplikatdetails = <span style=\"color:blue;\">New<\/span> Form_frmDuplikatdetails\r\n     <span style=\"color:blue;\">With<\/span> frmDuplikatdetails\r\n         .Filter = GetSinglePrimaryKey(Me!cboTabellen) _\r\n             & \" = \" & lngID\r\n         .FilterOn = <span style=\"color:blue;\">True<\/span>\r\n         .Tag = lngID\r\n         .Visible = <span style=\"color:blue;\">True<\/span>\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     colForms.Add frmDuplikatdetails, CStr(lngID)\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Duplikatdetailformulare ausblenden<\/h2>\n<p>Irgendwann hat der Benutzer die Details der Duplikatdatens&auml;tze zu Ende studiert und m&ouml;chte diese entweder einfach schlie&szlig;en oder aber er klickt auf die Schaltfl&auml;che <b>cmdAlsZielUebernehmen<\/b>, um einen der im Formular <b>frmDuplikatdetails <\/b>angezeigten Datens&auml;tze gleich als zu &uuml;bernehmenden Datensatz zu markieren.<\/p>\n<p>Wenn er das Detailformular einfach schlie&szlig;t, l&ouml;st er damit automatisch das Ereignis <b>Beim Schlie&szlig;en <\/b>des Formulars aus. Diese ruft die Methode <b>DuplikatformularAusblenden <\/b>des Formulars <b>frmDuplikatmanager <\/b>auf und &uuml;bergibt den Wert der <b>Tag<\/b>-Eigenschaft des Formulars, die den Prim&auml;rschl&uuml;sselwert des aktuellen Datensatzes enth&auml;lt:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Close()\r\n     Forms!frmDuplikatmanager.DuplikatformularAusblenden  Me.Tag\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Damit machen wir einen kleinen Sprung zur&uuml;ck zum Klassenmodul des Formulars <b>frmDuplikatmanager<\/b>, und zwar zur Methode <b>DuplikatformularAusblenden<\/b>. Diese sucht das Element aus der Collection <b>colForms <\/b>heraus, das dem auszublendenden Formular entspricht, und entfernt es aus der Collection:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>DuplikatformularAusblenden(strTag<span style=\"color:blue;\"> As String<\/span>)\r\n     On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n     colForms.Item(strTag).Tag = \"\"\r\n     colForms.Remove strTag\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Auf diese Weise verbleiben nur die Eintr&auml;ge in der Collection, f&uuml;r die auch noch ein ge&ouml;ffnetes Formular vorliegt.<\/p>\n<p>Die zweite Variante, mit welcher der Benutzer ein Detailformular schlie&szlig;en kann, ist ein Klick auf die Schaltfl&auml;che <b>cmdAlsZielUebernehmen <\/b>des Formulars <b>frmDuplikatdetails<\/b>. Diese liest den Inhalt der <b>Tag<\/b>-Eigenschaft des Formulars in die Variable <b>strTag <\/b>ein. Dann referenziert sie das aufrufende Formular <b>frmDuplikatmanager <\/b>und ruft die Methode <b>ZieldatensatzSetzen <\/b>mit dem Wert aus <b>strTag <\/b>auf:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdAlsZielUebernehmen_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>frm<span style=\"color:blue;\"> As <\/span>Form\r\n     <span style=\"color:blue;\">Dim <\/span>strTag<span style=\"color:blue;\"> As String<\/span>\r\n     strTag = Me.Tag\r\n     <span style=\"color:blue;\">Set<\/span> frm = Forms!frmDuplikatmanager\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> frm Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n         frm.ZieldatensatzSetzen strTag\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Diese Methode stellt den Datensatzzeiger im Unterformular <b>sfmDuplikateDetails <\/b>auf den entsprechenden Datensatz ein und ruft die Prozedur <b>Duplikatformular-Ausblenden <\/b>auf:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>ZieldatensatzSetzen(varZieldatensatzID<span style=\"color:blue;\"> As <\/span> Variant)\r\n     Me!sfmDuplikateDetails.Form.Recordset.FindFirst  GetSinglePrimaryKey(Me!cboTabellen)  & \"=\" & varZieldatensatzID\r\n     DuplikatformulareAusblenden\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die Methode <b>DuplikatformulareAusblenden<\/b> durchl&auml;uft alle aktuell ge&ouml;ffneten Formulare &uuml;ber die <b>Forms<\/b>-Auflistung. Dabei ruft sie zuerst die Prozedur <b>DuplikatformularAusblenden <\/b>auf (man beachte Plural und Singular bei der Benennung der beiden Prozeduren), die, wie oben beschrieben, den Eintrag f&uuml;r das aktuelle Formular aus der Collection <b>colForms <\/b>entfernt.<\/p>\n<p>Danach pr&uuml;ft es, ob das Formular <b>frmDuplikatdetails <\/b>hei&szlig;t, und schlie&szlig;t es dann:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>DuplikatformulareAusblenden()\r\n     <span style=\"color:blue;\">Dim <\/span>frm<span style=\"color:blue;\"> As <\/span>Form\r\n     <span style=\"color:blue;\">Dim <\/span>i<span style=\"color:blue;\"> As Integer<\/span>\r\n     For i = Forms.Count - 1 To 0 Step -1\r\n         <span style=\"color:blue;\">Set<\/span> frm = Forms(i)\r\n         DuplikatformularAusblenden frm.Tag\r\n         <span style=\"color:blue;\">If <\/span>frm.Name = \"frmDuplikatdetails\"<span style=\"color:blue;\"> Then<\/span>\r\n             DoCmd.Close acForm, frm.Name\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Next<\/span> i\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Duplikate zusammenf&uuml;hren<\/h2>\n<p>Fehlt nur noch die Prozedur, mit der Sie die mit den zu l&ouml;schenden Duplikaten verkn&uuml;pften Datens&auml;tze an das zu &uuml;bernehmende Duplikat binden und mit der Sie die &uuml;berfl&uuml;ssigen Datens&auml;tze l&ouml;schen.<\/p>\n<p>Dies erledigt die Prozedur aus Listing 6. Sie ermittelt das Prim&auml;rschl&uuml;sselfeld und den passenden Wert f&uuml;r das beizubehaltende Duplikat und erstellt dann ein Recordset, das alle Duplikate enth&auml;lt, die gel&ouml;scht werden sollen. Dieses durchl&auml;uft sie in einer <b>Do While<\/b>-Schleife und tr&auml;gt dabei zun&auml;chst die Inhalte des Listenfeldes <b>lstVerknuepf-te-Tabellen <\/b>in die Variablen <b>strForeignTable <\/b>und <b>strFK <\/b>ein. So erstellt sie eine <b>UPDATE<\/b>-Abfrage, die den Fremdschl&uuml;sselwert der betroffenen Datens&auml;tze der verkn&uuml;pften Tabellen mit der beizubehaltenden Tabelle verkn&uuml;pft. Anschlie&szlig;end l&ouml;scht die Prozedur das nun &uuml;berfl&uuml;ssige Duplikat. Dies erledigt die Prozedur f&uuml;r alle zu l&ouml;schenden Duplikate.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdUebernehmen_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database, rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Dim <\/span>strPK<span style=\"color:blue;\"> As String<\/span>, var<span style=\"color:blue;\"> As Variant<\/span>, lngPK<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strForeignTable<span style=\"color:blue;\"> As String<\/span>, strFK<span style=\"color:blue;\"> As String<\/span>, lngFK<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strSQL<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     strPK = GetSinglePrimaryKey(Me!cboTabellen)\r\n     lngPK = Me!sfmDuplikateDetails.Form.Controls(strPK)\r\n     <span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT * FROM \" & Me!sfmDuplikateDetails.Form.RecordSource & \" WHERE NOT \" _\r\n         & strPK & \" = \" & lngPK, dbOpenDynaset)\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rst.EOF\r\n         For Each var In Me!lstVerknuepfteTabellen.ItemsSelected\r\n             strForeignTable = Me!lstVerknuepfteTabellen.Column(0, var)\r\n             strFK = Me!lstVerknuepfteTabellen.Column(1, var)\r\n             lngFK = rst(strFK)\r\n             strSQL = \"UPDATE \" & strForeignTable & \" SET \" & strFK & \" = \" & lngPK & \" WHERE \" & strFK & \" = \" & lngFK\r\n             db.Execute strSQL, dbFailOnError\r\n         <span style=\"color:blue;\">Next<\/span> var\r\n         strSQL = \"DELETE FROM \" & Me!cboTabellen & \" WHERE \" & strPK & \" = \" & rst(strPK)\r\n         db.Execute strSQL, dbFailOnError\r\n         rst.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     DuplikatformulareAusblenden\r\n     Me!sfmDuplikateDetails.Requery\r\n     <span style=\"color:blue;\">Set<\/span> colForms = Nothing\r\n     <span style=\"color:blue;\">Set<\/span> colForms = <span style=\"color:blue;\">New<\/span> Collection\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 6: &uuml;berf&uuml;hren verkn&uuml;pfter Daten und L&ouml;schen &uuml;berfl&uuml;ssiger Datens&auml;tze<\/span><\/b><\/p>\n<p>Sofern noch eingeblendet, werden noch offene Detailformulare geschlossen und das Unterformular <b>sfmDuplikateDetails <\/b>aktualisiert. Dieses hat nun vermutlich einige Detaildatens&auml;tze in verkn&uuml;pften Tabellen mehr.<\/p>\n<h3>Downloads zu diesem Beitrag<\/h3>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>KundendatensaetzeZusammenfuehren.mdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/{5D1D8D90-7192-44B3-992C-18BD0757E277}\/aiu_1000.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wer eine Kundendatenbank pflegt, wird fr&uuml;her oder sp&auml;ter Dubletten in seiner Datenbank vorfinden. Sei es, weil Kunden sich mit neuer E-Mail und neuer Adresse erneut im Onlineshop anmelden und von dort importiert werden oder weil man bei der Suche nach einem vorhandenen Konto f&uuml;r einen Kunden wegen eines Tippfehlers keinen Treffer landet &#8211; langfristig lassen sich doppelte Kundendatens&auml;tze nicht verhindern. Aber das ist kein Problem: In Datenbanken l&auml;sst sich zum Gl&uuml;ck alles nachtr&auml;glich &auml;ndern. Wie dies bei Kundendaten und den damit verkn&uuml;pften Daten wie etwa Bestellungen funktioniert, erkl&auml;rt dieser Beitrag.<\/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":[662015,66052015,44000027],"tags":[],"class_list":["post-55001000","post","type-post","status-publish","format-standard","hentry","category-662015","category-66052015","category-Loesungen"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.9 (Yoast SEO v27.3) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Kundendatens&auml;tze zusammenf&uuml;hren - 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\/Kundendatensaetze_zusammenfuehren\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Kundendatens&auml;tze zusammenf&uuml;hren\" \/>\n<meta property=\"og:description\" content=\"Wer eine Kundendatenbank pflegt, wird fr&uuml;her oder sp&auml;ter Dubletten in seiner Datenbank vorfinden. Sei es, weil Kunden sich mit neuer E-Mail und neuer Adresse erneut im Onlineshop anmelden und von dort importiert werden oder weil man bei der Suche nach einem vorhandenen Konto f&uuml;r einen Kunden wegen eines Tippfehlers keinen Treffer landet - langfristig lassen sich doppelte Kundendatens&auml;tze nicht verhindern. Aber das ist kein Problem: In Datenbanken l&auml;sst sich zum Gl&uuml;ck alles nachtr&auml;glich &auml;ndern. Wie dies bei Kundendaten und den damit verkn&uuml;pften Daten wie etwa Bestellungen funktioniert, erkl&auml;rt dieser Beitrag.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Kundendatensaetze_zusammenfuehren\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2020-05-22T19:11:20+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg07.met.vgwort.de\/na\/2a96ee5668104d51ae65fd83b14e229b\" \/>\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=\"25\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Kundendatensaetze_zusammenfuehren\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Kundendatensaetze_zusammenfuehren\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Kundendatens&auml;tze zusammenf&uuml;hren\",\"datePublished\":\"2020-05-22T19:11:20+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Kundendatensaetze_zusammenfuehren\\\/\"},\"wordCount\":4012,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Kundendatensaetze_zusammenfuehren\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg07.met.vgwort.de\\\/na\\\/2a96ee5668104d51ae65fd83b14e229b\",\"articleSection\":[\"2015\",\"5\\\/2015\",\"L\u00f6sungen\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Kundendatensaetze_zusammenfuehren\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Kundendatensaetze_zusammenfuehren\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Kundendatensaetze_zusammenfuehren\\\/\",\"name\":\"Kundendatens&auml;tze zusammenf&uuml;hren - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Kundendatensaetze_zusammenfuehren\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Kundendatensaetze_zusammenfuehren\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg07.met.vgwort.de\\\/na\\\/2a96ee5668104d51ae65fd83b14e229b\",\"datePublished\":\"2020-05-22T19:11:20+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Kundendatensaetze_zusammenfuehren\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Kundendatensaetze_zusammenfuehren\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Kundendatensaetze_zusammenfuehren\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg07.met.vgwort.de\\\/na\\\/2a96ee5668104d51ae65fd83b14e229b\",\"contentUrl\":\"http:\\\/\\\/vg07.met.vgwort.de\\\/na\\\/2a96ee5668104d51ae65fd83b14e229b\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Kundendatensaetze_zusammenfuehren\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Kundendatens&auml;tze zusammenf&uuml;hren\"}]},{\"@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":"Kundendatens&auml;tze zusammenf&uuml;hren - 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\/Kundendatensaetze_zusammenfuehren\/","og_locale":"de_DE","og_type":"article","og_title":"Kundendatens&auml;tze zusammenf&uuml;hren","og_description":"Wer eine Kundendatenbank pflegt, wird fr&uuml;her oder sp&auml;ter Dubletten in seiner Datenbank vorfinden. Sei es, weil Kunden sich mit neuer E-Mail und neuer Adresse erneut im Onlineshop anmelden und von dort importiert werden oder weil man bei der Suche nach einem vorhandenen Konto f&uuml;r einen Kunden wegen eines Tippfehlers keinen Treffer landet - langfristig lassen sich doppelte Kundendatens&auml;tze nicht verhindern. Aber das ist kein Problem: In Datenbanken l&auml;sst sich zum Gl&uuml;ck alles nachtr&auml;glich &auml;ndern. Wie dies bei Kundendaten und den damit verkn&uuml;pften Daten wie etwa Bestellungen funktioniert, erkl&auml;rt dieser Beitrag.","og_url":"https:\/\/access-im-unternehmen.de\/Kundendatensaetze_zusammenfuehren\/","og_site_name":"Access im Unternehmen","article_published_time":"2020-05-22T19:11:20+00:00","og_image":[{"url":"http:\/\/vg07.met.vgwort.de\/na\/2a96ee5668104d51ae65fd83b14e229b","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"25\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/Kundendatensaetze_zusammenfuehren\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Kundendatensaetze_zusammenfuehren\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Kundendatens&auml;tze zusammenf&uuml;hren","datePublished":"2020-05-22T19:11:20+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Kundendatensaetze_zusammenfuehren\/"},"wordCount":4012,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Kundendatensaetze_zusammenfuehren\/#primaryimage"},"thumbnailUrl":"http:\/\/vg07.met.vgwort.de\/na\/2a96ee5668104d51ae65fd83b14e229b","articleSection":["2015","5\/2015","L\u00f6sungen"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Kundendatensaetze_zusammenfuehren\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Kundendatensaetze_zusammenfuehren\/","url":"https:\/\/access-im-unternehmen.de\/Kundendatensaetze_zusammenfuehren\/","name":"Kundendatens&auml;tze zusammenf&uuml;hren - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Kundendatensaetze_zusammenfuehren\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Kundendatensaetze_zusammenfuehren\/#primaryimage"},"thumbnailUrl":"http:\/\/vg07.met.vgwort.de\/na\/2a96ee5668104d51ae65fd83b14e229b","datePublished":"2020-05-22T19:11:20+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Kundendatensaetze_zusammenfuehren\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Kundendatensaetze_zusammenfuehren\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/Kundendatensaetze_zusammenfuehren\/#primaryimage","url":"http:\/\/vg07.met.vgwort.de\/na\/2a96ee5668104d51ae65fd83b14e229b","contentUrl":"http:\/\/vg07.met.vgwort.de\/na\/2a96ee5668104d51ae65fd83b14e229b"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Kundendatensaetze_zusammenfuehren\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Kundendatens&auml;tze zusammenf&uuml;hren"}]},{"@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\/55001000","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=55001000"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001000\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001000"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001000"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001000"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}