{"id":55001335,"date":"2021-12-01T00:00:00","date_gmt":"2021-12-10T07:39:55","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1335"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Assistent_fuer_mnBeziehungen","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Assistent_fuer_mnBeziehungen\/","title":{"rendered":"Assistent f&uuml;r m:n-Beziehungen"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg07.met.vgwort.de\/na\/4281bdcab75f4a3ea714d77a8d68b1c8\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>Microsoft Access bietet eine ganze Reihe von praktischen Assistenten. Ich setze beispielsweise sehr oft den Nachschlage-Assistenten ein, der nicht nur eine Beziehung mit den gew&uuml;nschten Optionen anlegt, sondern das bearbeitete Feld auch noch als Kombinationsfeld auslegt, mit dem die Daten der verkn&uuml;pften Tabelle leicht ausgew&auml;hlt werden k&ouml;nnen. Eine m:n-Beziehung stellen Sie her, indem Sie zwei solcher Nachschlage-felder in der sogenannten Verkn&uuml;pfungstabelle anlegen. Noch praktischer w&auml;re es, wenn Sie diese Verkn&uuml;pfungstabelle gar nicht erst anlegen m&uuml;ssten. Stattdessen w&auml;ren nur die zu verkn&uuml;pfenden Tabellen auszuw&auml;hlen und der Assistent erledigt den Rest &#8211; das Anlegen der Verkn&uuml;pfungstabelle mit den notwendigen Feldern sowie das Einrichten der Beziehungen zu den zu verkn&uuml;pfenden Tabellen. Dieser Beitrag zeigt, wie Sie einen solchen Assistenten programmieren k&ouml;nnen.<\/b><\/p>\n<h2>Ausgangsposition<\/h2>\n<p>Der Assistent soll zwei Tabellen wie die in Bild 1 miteinander verkn&uuml;pfen. Dazu ben&ouml;tigen wir eine weitere Tabelle etwa namens <b>tblProdukteKategorien <\/b>mit dem Prim&auml;rschl&uuml;sselfeld <b>ProduktKategorieID <\/b>und den beiden Fremdschl&uuml;sselfeldern <b>ProduktID <\/b>und <b>KategorieID<\/b>.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_001.png\" alt=\"Zu verkn&uuml;pfende Tabellen\" width=\"549,559\" height=\"197,8822\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Zu verkn&uuml;pfende Tabellen<\/span><\/b><\/p>\n<p>Diese stellen jeweils die Verkn&uuml;pfung zu den Tabellen <b>tblProdukte <\/b>und <b>tblKategorien <\/b>her, sodass alle Eintr&auml;ge der Tabelle <b>tblProdukte <\/b>mit den Eintr&auml;gen der Tabelle <b>tblKategorien <\/b>verkn&uuml;pft werden k&ouml;nnen. Die Fremdschl&uuml;sselfelder legen Sie am schnellsten an, indem Sie im Tabellenentwurf den Eintrag <b>Nachschlage-Assistent&#8230; <\/b>w&auml;hlen und die Verkn&uuml;pfung dar&uuml;ber vornehmen. Die Verkn&uuml;pfungstabelle sieht danach wie in Bild 2 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_002.png\" alt=\"Die Verkn&uuml;pfungstabelle\" width=\"649,559\" height=\"300,44\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Die Verkn&uuml;pfungstabelle<\/span><\/b><\/p>\n<p>Wechseln Sie in die Datenblattansicht, erhalten Sie eine Tabelle, mit deren Fremdschl&uuml;sselfeldern Sie die gew&uuml;nschten Kombinationen aus Produkten und Kategorien leicht zusammenstellen k&ouml;nnen (siehe Bild 3).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_003.png\" alt=\"Datenblattansicht der Verkn&uuml;pfung\" width=\"499,5589\" height=\"241,1664\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Datenblattansicht der Verkn&uuml;pfung<\/span><\/b><\/p>\n<p>Die Beziehungen k&ouml;nnen Sie danach wie in Bild 4 im Beziehungen-Fenster einsehen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_004.png\" alt=\"Die m:n-Beziehung im Beziehungen-Fenster\" width=\"599,559\" height=\"249,3011\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Die m:n-Beziehung im Beziehungen-Fenster<\/span><\/b><\/p>\n<h2>Vorher<\/h2>\n<p>Um eine m:n-Beziehung zwischen zwei Tabellen herzustellen, sind normalerweise die folgenden Schritte n&ouml;tig:<\/p>\n<ul>\n<li>Erstellen der Verkn&uuml;pfungstabelle<\/li>\n<li>Wahl eines Namens f&uuml;r die Verkn&uuml;pfungstabelle<\/li>\n<li>Hinzuf&uuml;gen eines Prim&auml;rschl&uuml;sselfeldes<\/li>\n<li>Hinzuf&uuml;gen eines Fremdschl&uuml;sselfeldes zum Verkn&uuml;pfen der Verkn&uuml;pfungstabelle mit der m-Tabelle<\/li>\n<li>Hinzuf&uuml;gen eines Fremdschl&uuml;sselfeldes zum Verkn&uuml;pfen der Verkn&uuml;pfungstabelle mit der n-Tabelle<\/li>\n<li>Aufrufen des Nachschlage-Assistenten zum Erstellen der Beziehung zwischen dem ersten Fremdschl&uuml;sselfeld und dem Prim&auml;rschl&uuml;sselfeld der m-Tabelle oder, falls kein Nachschlagefeld gew&uuml;nscht ist, sondern nur die reine Beziehung, manuelles Hinzuf&uuml;gen der Beziehung &uuml;ber das Beziehungen-Fenster. Au&szlig;erdem Einstellen der Beziehungseigenschaften wie referenzielle Integrit&auml;t, L&ouml;schweitergabe und Aktualisierungsweitergabe.<\/li>\n<li>Aufrufen des Nachschlage-Assistenten zum Erstellen der Beziehung zwischen dem zweiten Fremdschl&uuml;sselfeld und dem Prim&auml;rschl&uuml;sselfeld der n-Tabelle oder, falls kein Nachschlagefeld gew&uuml;nscht ist, sondern nur die reine Beziehung, manuelles Hinzuf&uuml;gen der Beziehung &uuml;ber das Beziehungen-Fenster. Au&szlig;erdem Einstellen der Beziehungseigenschaften wie referenzielle Integrit&auml;t, L&ouml;schweitergabe und Aktualisierungsweitergabe.<\/li>\n<li>Gegebenenfalls Hinzuf&uuml;gen weiterer Felder zur m:n-Verkn&uuml;pfungstabelle wie etwa Einzelpreis oder Menge bei einer Tabelle zum Speichern von Bestellpositionen<\/li>\n<\/ul>\n<h2>Anforderungen<\/h2>\n<p>Was genau ben&ouml;tigen wir an Informationen, um automatisiert eine Verkn&uuml;pfungstabelle f&uuml;r zwei per m:n-Beziehung zu verkn&uuml;pfenden Tabellen zu erstellen Beziehungsweise welche Informationen muss der Assistent zur Erstellung einer m:n-Beziehung vom Entwickler abfragen<\/p>\n<ul>\n<li>Name der ersten Tabelle<\/li>\n<li>Name des Prim&auml;rschl&uuml;sselfeldes der ersten Tabelle<\/li>\n<li>Gegebenenfalls Name des anzuzeigenden Feldes der ersten Tabelle<\/li>\n<li>Name der zweiten Tabelle<\/li>\n<li>Name des Prim&auml;rschl&uuml;sselfeldes der zweiten Tabelle<\/li>\n<li>Gegebenenfalls Name des anzuzeigenden Feldes der zweiten Tabelle<\/li>\n<li>Name der zu erstellenden Tabelle<\/li>\n<li>Namen f&uuml;r die Fremdschl&uuml;sselfelder zum Herstellen der Beziehung mit der m-Tabelle und der n-Tabelle<\/li>\n<li>Gegebenenfalls weitere Felder, die zur m:n-Tabelle hinzugef&uuml;gt werden sollen<\/li>\n<\/ul>\n<h2>Programmieren des Add-Ins<\/h2>\n<p>Starten wir also direkt mit der Programmierung des Add-Ins. Dieses soll &uuml;ber ein Formular verf&uuml;gen, mit dem wir alle Einstellungen f&uuml;r die Erstellung der m:n-Beziehung vornehmen k&ouml;nnen.<\/p>\n<h2>Auswahl der beteiligten Tabellen<\/h2>\n<p>Als Erstes ben&ouml;tigen wir zwei Kombinationsfelder, mit denen wir die beiden an der Beziehung beteiligten Tabellen ermitteln k&ouml;nnen.<\/p>\n<p>Diese Kombinationsfelder erhalten nach dem Hinzuf&uuml;gen zu einem neuen Formular namens <b>frmMNAssistent <\/b>die Bezeichnungen <b>cboMTabelle <\/b>und <b>cboNTabelle<\/b>. Diese f&uuml;gen wir jeweils in einen neuen Rahmen ein, sodass der Aufbau in der Entwurfsansicht zun&auml;chst wie in Bild 5 aussieht.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_005.png\" alt=\"Assistenten-Formular\" width=\"549,559\" height=\"340,0735\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Assistenten-Formular<\/span><\/b><\/p>\n<p>Damit der Benutzer die in der aktuellen Datenbank enthaltenen Tabellen ausw&auml;hlen kann, stellen wir die Eigenschaft <b>Datensatzherkunft <\/b>der beiden Kombinationsfelder auf die Abfrage aus Bild 6 ein.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_006.png\" alt=\"Datensatzherkunft der Kombinationsfelder zur Auswahl der an der Beziehung beteiligten Tabellen\" width=\"599,559\" height=\"383,5627\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Datensatzherkunft der Kombinationsfelder zur Auswahl der an der Beziehung beteiligten Tabellen<\/span><\/b><\/p>\n<p>Damit die hier verwendete Tabelle <b>MSysObjects<\/b> sichtbar ist, m&uuml;ssen Sie zun&auml;chst die entsprechende Option aktivieren. Diese finden Sie, wenn Sie mit der rechten Maustaste auf den Navigationsbereich klicken und dann den Eintrag <b>Navigationsoptionen&#8230; <\/b>ausw&auml;hlen. Im nun erscheinenden Dialog <b>Navigationsoptionen <\/b>aktivieren Sie die Option <b>Systemobjekte anzeigen<\/b>.<\/p>\n<p>Danach k&ouml;nnen Sie der Abfrage f&uuml;r die Eigenschaft <b>Datensatzherkunft <\/b>wie in der Abbildung die Tabelle <b>MSysObjects <\/b>hinzuf&uuml;gen und die beiden Felder <b>Name <\/b>und <b>Type <\/b>zum Entwurfsraster der Abfrage hinzuf&uuml;gen. Au&szlig;erdem stellen Sie als Kriterium f&uuml;r das Feld Name den Wert <b>Nicht Wie &#8222;MSys*&#8220; Und Nicht Wie &#8222;USys*&#8220; Und Nicht Wie &#8222;f_*&#8220; <\/b>und f&uuml;r das Feld <b>Type <\/b>den Wert <b>1 <\/b>ein. Das sorgt  erstens daf&uuml;r, dass keine System- und tempor&auml;ren Tabellen erscheinen und dass nur lokale Tabellen zur Auswahl angeboten werden.<\/p>\n<p>Nach dem Schlie&szlig;en des Abfrageentwurfs k&ouml;nnen Sie den Wert der Eigenschaft <b>Datensatzherkunft <\/b>des Kombinationsfeldes <b>cboMTabelle <\/b>in die entsprechende Eigenschaft des Kombinationsfeldes <b>cboNTabelle <\/b>kopieren:<\/p>\n<pre>SELECT Name, Type \r\nFROM MSysObjects \r\nWHERE (Name <span style=\"color:blue;\">Not<\/span> Like \"MSys*\" \r\nAnd Name <span style=\"color:blue;\">Not<\/span> Like \"USys*\" \r\nAnd Name <span style=\"color:blue;\">Not<\/span> Like \"f_*\") \r\nAND (Type=1);<\/pre>\n<p>Wenn Sie anschlie&szlig;end in die Formularansicht wechseln, k&ouml;nnen Sie bereits die an der Beziehung beteiligten Tabellen ausw&auml;hlen (siehe Bild 7).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_007.png\" alt=\"Auswahl der an der Beziehung beteiligten Kombinationsfelder\" width=\"524,559\" height=\"266,6654\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 7: Auswahl der an der Beziehung beteiligten Kombinationsfelder<\/span><\/b><\/p>\n<h2>Auswahl der beteiligten Prim&auml;rschl&uuml;sselfelder<\/h2>\n<p>Unter den beiden Kombinationsfeldern platzieren wir zwei weitere Kombinationsfelder namens <b>cboMPrimaerschluessel <\/b>und <b>cboNPrimaerschluessel<\/b>.<\/p>\n<p>Diese sollen direkt nach der Auswahl einer Tabelle mit dem dar&uuml;ber befindlichen Kombinationsfeld alle Felder der gew&auml;hlten Tabelle anzeigen und das Prim&auml;rschl&uuml;sselfeld dieser Tabelle als Vorauswahl einstellen.<\/p>\n<p>Dazu m&uuml;ssen wir zwei Aufgaben erledigen:<\/p>\n<ul>\n<li>eine Liste aller Felder der jeweiligen Tabelle zusammenstellen und<\/li>\n<li>das Prim&auml;rschl&uuml;sselfeld aus diesen Feldern ermitteln.<\/li>\n<\/ul>\n<p>Die Liste der Felder k&ouml;nnen wir auf zwei Wegen herausfinden. Der erste verwendet ausschlie&szlig;lich VBA und nutzt die <b>Fields<\/b>-Auflistung des jeweiligen <b>TableDef<\/b>-Objekts.<\/p>\n<p>Der zweite verwendet die Herkunftsart <b>Feldliste<\/b> und die im ersten Kombinationsfeld ausgew&auml;hlte Tabelle als <b>Datensatzherkunft<\/b>. Dabei m&uuml;ssen wir aber dennoch nach der Auswahl VBA bem&uuml;hen, um die Eigenschaft <b>Datensatzherkunft <\/b>zu f&uuml;llen. Um nur das zu erledigen, hinterlegen wir die folgenden beiden Ereignisprozeduren jeweils f&uuml;r Kombinationsfelder <b>cboMTabelle <\/b>und <b>cboNTabelle<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cboMTabelle_AfterUpdate()\r\n     Me!cboMPrimaerschluessel.RowSource = Me!cboMTabelle\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>cboNTabelle_AfterUpdate()\r\n     Me!cboNPrimaerschluessel.RowSource = Me!cboNTabelle\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Damit bieten die beiden Kombinationsfelder bereits die Felder der jeweiligen Tabelle zur Auswahl an (siehe Bild 8).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_008.png\" alt=\"Auswahl des Prim&auml;rschl&uuml;sselfeldes f&uuml;r das Erstellen der m:n-Beziehung\" width=\"574,559\" height=\"247,5811\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 8: Auswahl des Prim&auml;rschl&uuml;sselfeldes f&uuml;r das Erstellen der m:n-Beziehung<\/span><\/b><\/p>\n<h2>Prim&auml;rschl&uuml;sselfeld ausw&auml;hlen<\/h2>\n<p>Nun wollen wir noch daf&uuml;r sorgen, dass direkt die Prim&auml;rschl&uuml;sselfelder dieser Tabellen angezeigt werden. Schlie&szlig;lich soll der Benutzer so wenig Aufwand wie m&ouml;glich haben. Dazu f&uuml;gen wir den beiden Ereignisprozeduren <b>cboMTabelle_AfterUpdate <\/b>und <b>cboNTabelle_AfterUpdate <\/b>jeweils einen Aufruf einer Funktion namens <b>Primaerschluessel-Ermitteln <\/b>hinzu:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cboMTabelle_AfterUpdate()\r\n     Me!cboMPrimaerschluessel.RowSource = Me!cboMTabelle\r\n     Me!cboMPrimaerschluessel =  PrimaerschluesselErmitteln(Me!cboMTabelle)\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Diese Funktion erwartet den Namen der zu untersuchenden Tabelle als Parameter. Sie f&uuml;llt die Variable <b>db <\/b>mit einem Verweis auf das aktuelle <b>Database<\/b>-Objekt und <b>tdf <\/b>mit einem Verweis auf das <b>TableDef<\/b>-Objekt f&uuml;r die Tabelle aus <b>strTabelle<\/b>. Dann durchl&auml;uft sie alle Eintr&auml;ge der Auflistung <b>tdf.Indexes <\/b>und speichert das jeweils aktuelle Element in der Variablen <b>idx<\/b>. F&uuml;r dieses pr&uuml;ft sie den Wert der Eigenschaft <b>Primary<\/b>.<\/p>\n<p>Ist dieser <b>True<\/b>, schreibt sie den Namen des ersten Feldes dieses Indizes in den R&uuml;ckgabewert der Funktion und beendet diese mit <b>Exit Function<\/b>. Das funktioniert f&uuml;r keinen, einen oder zusammengesetzte Prim&auml;rschl&uuml;ssel gleicherma&szlig;en sinnvoll: Wenn kein Prim&auml;rschl&uuml;ssel f&uuml;r die Tabelle vorhanden ist, liefert die Funktion eine leere Zeichenkette zur&uuml;ck, f&uuml;r einen Prim&auml;rschl&uuml;ssel mit einem Feld den Namen des betroffenen Feldes und f&uuml;r einen zusammengesetzten Prim&auml;rsch&uuml;ssel den Namen des ersten Feldes:<\/p>\n<pre><span style=\"color:blue;\">Private Function <\/span>PrimaerschluesselErmitteln( strTabelle<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>tdf<span style=\"color:blue;\"> As <\/span>DAO.TableDef\r\n     <span style=\"color:blue;\">Dim <\/span>idx<span style=\"color:blue;\"> As <\/span>DAO.Index\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> tdf = db.TableDefs(strTabelle)\r\n     For Each idx In tdf.Indexes\r\n         <span style=\"color:blue;\">If <\/span>idx.Primary<span style=\"color:blue;\"> Then<\/span>\r\n             PrimaerschluesselErmitteln =  idx.Fields(0).Name\r\n             <span style=\"color:blue;\">Exit Function<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Next<\/span> idx\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>F&uuml;r unser Beispiel mit den Produkten und Kategorien stellen die Prozeduren zuverl&auml;ssig das jeweilige Prim&auml;rsch&uuml;sselfeld ein.<\/p>\n<h2>Informationen f&uuml;r das Nachschlagefeld<\/h2>\n<p>Zus&auml;tzlich wollen wir in diesem Formular die Daten f&uuml;r die Nachschlagefelder abfragen, sofern der Benutzer diese anlegen m&ouml;chte. Manchmal ist es sinnvoll, zum Beispiel bei Bestellpositionen, wo man direkt aus der m:n-Verkn&uuml;pfungstabelle das Produkt zu einer Bestellposition ausw&auml;hlen m&ouml;chte. An anderen Stellen sind die Werte der Verkn&uuml;pfungstabelle vielleicht gar nicht sichtbar, zum Beispiel wenn diese &uuml;ber zwei Listenfelder angezeigt werden.<\/p>\n<p>Also f&uuml;gen wir die notwendigsten Felder hinzu plus einem Kontrollk&auml;stchen zum Aktivieren oder Deaktivieren dieser Einstellungen.<\/p>\n<p>Die Kontrollk&auml;stchen hei&szlig;en <b>chkMNachschlagefeld <\/b>und <b>chkNNachschlagefeld<\/b>, die Kombinationsfelder zur Auswahl des anzuzeigenden Feldes f&uuml;r das Nachschlagefeld hei&szlig;en <b>cboMNachschlagefeld <\/b>und <b>cboNNachschlagefeld<\/b> (siehe Bild 9). Damit die beiden Kombinationsfelder genau wie die zur Auswahl der Prim&auml;rschl&uuml;sselfelder auch die Felder der zu verkn&uuml;pfenden Tabellen anzeigen, f&uuml;gen wir den Prozeduren <b>cboMTabelle_AfterUpdate <\/b>und <b>cboNTabelle_AfterUpdate <\/b>noch jeweils eine Anweisung hinzu:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_009.png\" alt=\"Informationen f&uuml;r die Nachschlagefelder\" width=\"624,559\" height=\"311,8127\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 9: Informationen f&uuml;r die Nachschlagefelder<\/span><\/b><\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cboMTabelle_AfterUpdate()\r\n     ...\r\n     Me!cboMNachschlagefeld.RowSource = Me!cboMTabelle\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>cboNTabelle_AfterUpdate()\r\n     ...\r\n     Me!cboNNachschlagefeld.RowSource = Me!cboNTabelle\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Damit die beiden Kontrollk&auml;stchen beim Aktivieren und Deaktivieren auch die beiden Kombinationsfelder <b>cboMNachschlagefeld <\/b>und <b>cboNNachschlagefeld <\/b>aktivieren beziehungsweise deaktivieren, f&uuml;gen wir diese Ereignisprozeduren f&uuml;r das Ereignis <b>Nach Aktualisierung <\/b>der beiden Kontrollk&auml;stchen hinzu:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>chkMNachschlagefeld_AfterUpdate()\r\n     Me!cboMNachschlagefeld.Enabled = Me!chkMNachschlagefeld\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>chkNNachschlagefeld_AfterUpdate()\r\n     Me!cboNNachschlagefeld.Enabled = Me!chkNNachschlagefeld\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Name f&uuml;r die zu erstellende Tabelle und ihre Felder ermitteln<\/h2>\n<p>Die Bezeichnungen f&uuml;r die zu erstellende Tabelle und die enthaltenen Felder bestimmen wir automatisch auf Basis der gew&auml;hlten zu verkn&uuml;pfenden Tabellen und den gew&auml;hlten Prim&auml;rschl&uuml;sselfeldern. Dies erledigen wir in der Prozedur <b>VerknuepfungstabelleAktualisieren<\/b> aus Listing 1. Sie liest zuerst die gew&auml;hlten Tabellen in die Variablen <b>strMTabelle <\/b>und <b>strNTabelle <\/b>ein sowie die selektierten Prim&auml;rschl&uuml;sselfelder.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>VerknuepfungstabelleAktualisieren()\r\n     <span style=\"color:blue;\">Dim <\/span>strMTabelle<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strNTabelle<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strMPrimaerschluessel<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strNPrimaerschluessel<span style=\"color:blue;\"> As String<\/span>\r\n     strMTabelle = Nz(Me!cboMTabelle, \"\")\r\n     strNTabelle = Nz(Me!cboNTabelle, \"\")\r\n     strMPrimaerschluessel = Nz(Me!cboMPrimaerschluessel, \"\")\r\n     strNPrimaerschluessel = Nz(Me!cboNPrimaerschluessel, \"\")\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Left<\/span>(strMTabelle, 4) = \"tbl_\"<span style=\"color:blue;\"> Then<\/span>\r\n         strMTabelle = <span style=\"color:blue;\">Mid<\/span>(strMTabelle, 5)\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Left<\/span>(strMTabelle, 3) = \"tbl\"<span style=\"color:blue;\"> Then<\/span>\r\n         strMTabelle = <span style=\"color:blue;\">Mid<\/span>(strMTabelle, 4)\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Left<\/span>(strNTabelle, 4) = \"tbl_\"<span style=\"color:blue;\"> Then<\/span>\r\n         strNTabelle = <span style=\"color:blue;\">Mid<\/span>(strNTabelle, 5)\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Left<\/span>(strNTabelle, 3) = \"tbl\"<span style=\"color:blue;\"> Then<\/span>\r\n         strNTabelle = <span style=\"color:blue;\">Mid<\/span>(strNTabelle, 4)\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     Me!txtVerknuepfungstabelle = \"tbl\" & strMTabelle & strNTabelle\r\n     <span style=\"color:blue;\">If <\/span>strMPrimaerschluessel = \"ID\"<span style=\"color:blue;\"> Then<\/span>\r\n         strMPrimaerschluessel = strMTabelle & \"ID\"\r\n     <span style=\"color:blue;\">Else<\/span>If <span style=\"color:blue;\">Right<\/span>(strMPrimaerschluessel, 2) = \"ID\" Then\r\n         strMPrimaerschluessel = <span style=\"color:blue;\">Left<\/span>(strMPrimaerschluessel, <span style=\"color:blue;\">Len<\/span>(strMPrimaerschluessel) - 2)\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">If <\/span>strNPrimaerschluessel = \"ID\"<span style=\"color:blue;\"> Then<\/span>\r\n         strNPrimaerschluessel = strNTabelle & \"ID\"\r\n     <span style=\"color:blue;\">Else<\/span>If <span style=\"color:blue;\">Right<\/span>(strNPrimaerschluessel, 2) = \"ID\" Then\r\n         strNPrimaerschluessel = <span style=\"color:blue;\">Left<\/span>(strNPrimaerschluessel, <span style=\"color:blue;\">Len<\/span>(strNPrimaerschluessel) - 2)\r\n     End If\r\n     Me!txtMFremdschluesselfeld = strMPrimaerschluessel & \"ID\"\r\n     Me!txtNFremdschluesselfeld = strNPrimaerschluessel & \"ID\"\r\n     Me!txtPrimaerschluesselfeld = strMPrimaerschluessel & strNPrimaerschluessel & \"ID\"\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Einstellen der Daten der Verkn&uuml;pfungstabelle<\/span><\/b><\/p>\n<p>Dann folgt die Pr&uuml;fung, ob der Benutzer eines der Pr&auml;fixe <b>tbl <\/b>oder <b>tbl_ <\/b>f&uuml;r die beiden zu verkn&uuml;pfenden Tabellen verwendet. Falls ja, werden diese vorn abgeschnitten, sodass beispielsweise von <b>tblProdukte <\/b>oder <b>tbl_Produkte <\/b>nur noch <b>Produkte <\/b>&uuml;brig bleibt.<\/p>\n<p>Daraus leitet die Prozedur den Namen der Verkn&uuml;pfungstabelle ab, der aus dem Pr&auml;fix <b>tbl<\/b>, dem Namen der m-Tabelle und dem Namen der n-Tabelle jeweils ohne Pr&auml;fix besteht.<\/p>\n<p>Das Ergebnis landet im Textfeld <b>txtVerknuepfungstabelle<\/b>.<\/p>\n<p>Dann untersucht die Prozedur die gew&auml;hlten Prim&auml;rschl&uuml;sselfelder der beiden Tabellen. Diese dienen schlie&szlig;lich als Grundlage f&uuml;r die Benennung der Fremdschl&uuml;sselfelder der Verkn&uuml;pfungstabelle.<\/p>\n<p>Wenn der Name eines der Prim&auml;rschl&uuml;sselfelder lediglich <b>ID <\/b>lautet, stellt die Prozedur den oben ermittelten Namen der Tabelle ohne Pr&auml;fix voran. Das kann in unserem Fall auch der Plural sein, was zu Bezeichnungen wie <b>ProdukteID <\/b>oder <b>KategorienID <\/b>f&uuml;hren kann. Das ist aber kein Problem, denn der Benutzer kann die Bezeichnungen ja noch anpassen.<\/p>\n<p>Sollte die Bezeichnung des Prim&auml;rschl&uuml;sselfeldes jedoch der von uns erwarteten Konvention entsprechen, also aus dem Singular der Bezeichnung der enthaltenen Objekte bestehen (wie <b>ProduktID <\/b>oder <b>KategorieID<\/b>), dann speichert die Prozedur den Teil vor <b>ID <\/b>in den Variablen <b>strMPrimaerschluessel <\/b>und <b>strNPrimaerschluessel<\/b>. Anschlie&szlig;end h&auml;ngen wir hinten wieder <b>ID <\/b>an und schreiben das Ergebnis in die Textfelder <b>txtMFremdschluesselfeld <\/b>und <b>txtNFremdschluesselfeld<\/b>. Warum haben wir ID erst abgetrennt Weil wir noch das Prim&auml;rschl&uuml;sselfeld f&uuml;r die Verkn&uuml;pfungstabelle benennen m&uuml;ssen, dass den Singular der Bezeichnungen der Elemente der ersten und der zweiten Tabelle und das Suffix <b>ID <\/b>enthalten soll, zum Beispiel <b>ProduktKategorieID<\/b>. Dieser Ausdruck landet schlie&szlig;lich im Textfeld <b>txtPrimaerschluesselfeld<\/b>.<\/p>\n<p>Das Ergebnis sieht schlie&szlig;lich wie in Bild 10 aus. Damit dies wie gew&uuml;nscht funktioniert, f&uuml;gen wir den Aufruf dieser Prozedur in die folgenden beiden Prozeduren nachtr&auml;glich ein:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_010.png\" alt=\"Das Formular frmMNAssistent in der Formularansicht\" width=\"574,559\" height=\"361,5685\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 10: Das Formular frmMNAssistent in der Formularansicht<\/span><\/b><\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cboMTabelle_AfterUpdate()\r\n     ...\r\n     VerknuepfungstabelleAktualisieren\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>cboNTabelle_AfterUpdate()\r\n     ...\r\n     VerknuepfungstabelleAktualisieren\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>F&uuml;r die Aktualisierung der beiden Kombinationsfelder <b>cboMPrimaerschluessel <\/b>und <b>cboNPrimaerschluessel <\/b>legen wir die folgenden Ereignisprozeduren an:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cboNPrimaerschluessel_AfterUpdate()\r\n     VerknuepfungstabelleAktualisieren\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>cboMPrimaerschluessel_AfterUpdate()\r\n     VerknuepfungstabelleAktualisieren\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Pr&uuml;fen, ob Verkn&uuml;pfungstabelle bereits vorhanden ist<\/h2>\n<p>Bevor wir gleich die Verkn&uuml;pfungstabelle mit den angegebenen Feldern und Indizes anlegen, pr&uuml;fen wir, ob es bereits eine Tabelle mit dem angegebenen Namen gibt.<\/p>\n<p>Dazu verwenden wir die folgende Funktion namens <b>ExistsTable<\/b>, die als Parameter den Namen der zu untersuchenden Tabelle entgegennimmt:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>ExistsTable(strTable<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As Boolean<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>dao.Database\r\n     <span style=\"color:blue;\">Dim <\/span>tdf<span style=\"color:blue;\"> As <\/span>dao.TableDef\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> tdf = db.TableDefs(strTable)\r\n     <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> tdf Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n         ExistsTable = <span style=\"color:blue;\">True<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><!--30percent--><\/p>\n<p>Die Funktion erstellt ein <b>Database<\/b>-Objekt und versucht dann, bei deaktivierter eingebauter Fehlerbehandlung auf das <b>Table-Def<\/b>-Objekt mit dem Namen der zu untersuchenden Tabelle zuzugreifen. Gelingt das, ist <b>tdf <\/b>anschlie&szlig;end nicht leer, was dazu f&uuml;hrt, dass der Funktionswert in der <b>If&#8230;Then<\/b>-Bedingung auf den Wert <b>True <\/b>eingestellt wird.<\/p>\n<h2>Vorhandene Tabelle l&ouml;schen<\/h2>\n<p>Ist die Tabelle bereits vorhanden, fragen wir den Benutzer per Meldungsfenster, ob die vorhandene Tabelle gel&ouml;scht werden soll. Ist das der Fall, initiieren wir den L&ouml;schvorgang mit der folgenden Funktion. Diese ist auch f&uuml;r den Fall ausgestattet, dass die Tabelle aktuell ge&ouml;ffnet ist (siehe Listing 2).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>DeleteTable(strTable<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As Boolean<\/span>\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>lngError<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strError<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     DoCmd.Close acTable, strTable, acSaveNo\r\n     On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n     db.TableDefs.Delete strTable\r\n     lngError = Err.Number\r\n     strError = Err.Description\r\n     <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> lngError = 0<span style=\"color:blue;\"> Then<\/span>\r\n         Select Case lngError\r\n             <span style=\"color:blue;\">Case Else<\/span>\r\n                 <span style=\"color:blue;\">MsgBox<\/span> lngError & \" \" & strError\r\n         <span style=\"color:blue;\">End Select<\/span>\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         Application.RefreshDatabaseWindow\r\n         DeleteTable = <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: L&ouml;schen der angegebenen Tabelle<\/span><\/b><\/p>\n<p>Die Funktion nimmt den Namen der Tabelle mit dem Parameter <b>strTable <\/b>entgegen. Als erste Aktion schlie&szlig;t sie die gegebenenfalls noch ge&ouml;ffnete Tabelle mit der Methode <b>DoCmd.Close<\/b>.<\/p>\n<p>F&uuml;r den dritten Parameter <b>Save <\/b>geben wir den Wert <b>acSaveNo <\/b>an, der daf&uuml;r sorgt, dass im Falle offener &Auml;nderungen weder ein Speichern der &Auml;nderungen noch eine entsprechende R&uuml;ckfrage erfolgt.<\/p>\n<p>Dann deaktiviert die Funktion die Fehlerbehandlung und versucht mit der <b>Delete<\/b>-Methode der <b>TableDefs<\/b>-Auflistung, die Tabelle zu l&ouml;schen. Dies kann schiefgehen, wenn zum Beispiel noch ein Formular auf dieses Element zugreift. In diesem Fall zeigt die Prozedur die entsprechende Fehlermeldung an, die wir zuvor neben der Fehlernummer in den beiden Variablen <b>lngError <\/b>und <b>strError <\/b>gespeichert haben.<\/p>\n<p>Ist kein Fehler aufgetreten, wurde die Tabelle erfolgreich gel&ouml;scht. In diesem Fall sorgt ein Aufruf der Methode <b>Application.RefreshDatabaseWindow <\/b>daf&uuml;r, dass die gel&ouml;schte Tabelle direkt aus dem Navigationsbereich verschwindet. Au&szlig;erdem stellt die Funktion ihren R&uuml;ckgabewert auf <b>True <\/b>ein.<\/p>\n<h2>Verkn&uuml;pfungstabelle erstellen<\/h2>\n<p>Damit kommen wir endlich zu der Funktion, mit der wir die neue Verkn&uuml;pfungstabelle erstellen. Diese hei&szlig;t <b>AddManyToManyTable <\/b>und erwartet die folgenden Parameter:<\/p>\n<ul>\n<li><b>strLinkTable<\/b>: Name der zu erstellenden Verkn&uuml;pfungstabelle<\/li>\n<li><b>strPrimaryKey<\/b>: Name des zu erstellenden Prim&auml;rschl&uuml;sselfeldes<\/li>\n<li><b>strForeignKeyM<\/b>: Name des Fremdschl&uuml;sselfeldes zur ersten verkn&uuml;pften Tabelle<\/li>\n<li><b>strForeignKeyN<\/b>: Name des Fremdschl&uuml;sselfeldes zur zweiten verkn&uuml;pften Tabelle<\/li>\n<\/ul>\n<p>Die Funktion <b>AddManyToManyTable<\/b> finden Sie in Listing 3. Sie erstellt zun&auml;chst einen Verweis auf das aktuelle <b>Database<\/b>-Objekt. Dann legt sie ein neues <b>TableDef<\/b>-Objekt mit dem Namen aus <b>strLinkTable<\/b> an.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>AddManyToManyTable(strLinkTable<span style=\"color:blue;\"> As String<\/span>, strPrimaryKey<span style=\"color:blue;\"> As String<\/span>, _\r\n         strForeignKeyM<span style=\"color:blue;\"> As String<\/span>, strForeignKeyN<span style=\"color:blue;\"> As String<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>tdf<span style=\"color:blue;\"> As <\/span>DAO.TableDef\r\n     <span style=\"color:blue;\">Dim <\/span>fld<span style=\"color:blue;\"> As <\/span>DAO.Field\r\n     <span style=\"color:blue;\">Dim <\/span>idx<span style=\"color:blue;\"> As <\/span>DAO.Index\r\n     On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> tdf = db.CreateTableDef(strLinkTable)\r\n     <span style=\"color:blue;\">Set<\/span> fld = tdf.CreateField(strPrimaryKey, dbLong)\r\n     fld.Attributes = fld.Attributes + dbAutoIncrField\r\n     tdf.Fields.Append fld\r\n     <span style=\"color:blue;\">Set<\/span> idx = tdf.CreateIndex(\"PrimaryKey\")\r\n     <span style=\"color:blue;\">With<\/span> idx\r\n         .Primary = <span style=\"color:blue;\">True<\/span>\r\n         .Fields.Append .CreateField(strPrimaryKey)\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     tdf.Indexes.Append idx\r\n     <span style=\"color:blue;\">Set<\/span> fld = tdf.CreateField(strForeignKeyM, dbLong)\r\n     tdf.Fields.Append fld\r\n     <span style=\"color:blue;\">Set<\/span> fld = tdf.CreateField(strForeignKeyN, dbLong)\r\n     tdf.Fields.Append fld\r\n     <span style=\"color:blue;\">Set<\/span> idx = tdf.CreateIndex(\"UniqueKey\")\r\n     <span style=\"color:blue;\">With<\/span> idx\r\n         .Unique = <span style=\"color:blue;\">True<\/span>\r\n         .Fields.Append .CreateField(strForeignKeyM)\r\n         .Fields.Append .CreateField(strForeignKeyN)\r\n     End <span style=\"color:blue;\">With<\/span>\r\n     tdf.Indexes.Append idx\r\n     db.TableDefs.Append tdf\r\n     db.TableDefs.Refresh\r\n     <span style=\"color:blue;\">If <\/span>Err.Number = 0<span style=\"color:blue;\"> Then<\/span>\r\n         Application.RefreshDatabaseWindow\r\n         AddManyToManyTable = <span style=\"color:blue;\">True<\/span>\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> Err.Number & <span style=\"color:blue;\">vbCrLf<\/span> & <span style=\"color:blue;\">vbCrLf<\/span> & Err.Description\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 3: Anlegen der Verkn&uuml;pfungstabelle<\/span><\/b><\/p>\n<p>Anschlie&szlig;end erstellt sie das Prim&auml;rschl&uuml;sselfeld mit der <b>CreateField<\/b>-Methode des neuen <b>TableDef<\/b>-Objekts und gibt diesem den Namen aus <b>strPrimaryKey <\/b>und den Datentyp <b>Long<\/b>. Dann erweitert sie die Attribute &uuml;ber die Eigenschaft <b>Attributes <\/b>um das Attribut <b>dbAutoIncrField<\/b>, damit wir ein Autowertfeld erhalten und h&auml;ngt das Feld mit der <b>Append<\/b>-Methode an die Auflistung <b>Fields <\/b>der neuen Tabelle an.<\/p>\n<p>Danach erstellt die Tabelle den Index namens <b>PrimaryKey <\/b>f&uuml;r das Prim&auml;rschl&uuml;sselfeld. Um den Index als Prim&auml;rindex zu definieren, stellen wir die Eigenschaft <b>Primary <\/b>auf den Wert <b>True <\/b>ein.<\/p>\n<p>Schlie&szlig;lich h&auml;ngen wir dem Index das neue Feld mit dem Namen aus <b>strPrimaryKey <\/b>mit der <b>Append<\/b>-Methode an die <b>Fields<\/b>-Auflistung an und f&uuml;gen den Index wiederum mit Append der <b>Indexes<\/b>-Auflistung des <b>TableDef<\/b>-Objekts an.<\/p>\n<p>Wenn wir schon beim Erstellen von Indizes sind, legen wir auch gleich noch den zusammengesetzten, eindeutigen Index f&uuml;r die beiden Fremdschl&uuml;sselfelder der Tabelle an. Diesen erstellen wir mit <b>CreateIndex <\/b>unter dem Namen <b>UniqueKey<\/b>, stellen die Eigenschaft <b>Unique <\/b>auf <b>Ja <\/b>ein und f&uuml;gen die beiden Felder mit den Namen aus den Parametern <b>strForeignKeyM <\/b>und <b>strForeignKeyN <\/b>an.<\/p>\n<p>Danach f&uuml;gen wir auch diesen Index mit der <b>Append<\/b>-Methode an die Auflistung <b>Indexes <\/b>der Tabelle an.<\/p>\n<p>Anschlie&szlig;end folgen die beiden Fremdschl&uuml;sselfelder, die wir mit <b>CreateField <\/b>erstellen und dabei die Namen aus <b>strForeignKeyM <\/b>und <b>strForeignKeyN <\/b>sowie den Datentyp <b>dbLong <\/b>&uuml;bergeben. Nun h&auml;ngen wir das neue <b>TableDef<\/b>-Objekt an die <b>TableDefs<\/b>-Auflistung an und aktualisieren diese mit der <b>Refresh<\/b>-Methode.<\/p>\n<p>Ist w&auml;hrend all dieser Anweisungen kein Fehler aufgetreten, aktualisiert die Funktion die Anzeige im Navigationsbereich und gibt den Wert <b>True <\/b>als Funktionswert zur&uuml;ck. Ist doch ein Fehler aufgetreten, gibt die Funktion eine entsprechende Fehlermeldung aus.<\/p>\n<h2>Verkn&uuml;pfungstabelle per Mausklick<\/h2>\n<p>Nun fehlt noch die Ereignisprozedur f&uuml;r die Schaltfl&auml;che <b>cmdVerknuepfungstabelleAnlegen<\/b>, welche die drei zuvor definierten Funktionen zusammenbringt (siehe Listing 4).<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdVerknuepfungstabelleAnlegen_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>strMNTabelle<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>bolNeuErstellen<span style=\"color:blue;\"> As Boolean<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>bolNeuErstellt<span style=\"color:blue;\"> As Boolean<\/span>\r\n     strMNTabelle = Me!txtVerknuepfungstabelle\r\n     <span style=\"color:blue;\">If <\/span>ExistsTable(strMNTabelle) = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         If <span style=\"color:blue;\">MsgBox<\/span>(\"Die Tabelle \" & strMNTabelle & \" ist bereits vorhanden. Soll diese &uuml;berschrieben werden\", _\r\n                 vbYesNo + vbExclamation, \"Tabelle bereits vorhanden\") = vbYes Then\r\n             <span style=\"color:blue;\">If <\/span>DeleteTable(strMNTabelle) = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n                 bolNeuErstellen = <span style=\"color:blue;\">True<\/span>\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         bolNeuErstellen = <span style=\"color:blue;\">True<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">If <\/span>bolNeuErstellen = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         If AddManyToManyTable(strMNTabelle, Me!txtPrimaerschluesselfeld, Me!txtNFremdschluesselfeld, _\r\n                 Me!txtMFremdschluesselfeld) = <span style=\"color:blue;\">True<\/span> Then\r\n             bolNeuErstellt = <span style=\"color:blue;\">True<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">If <\/span>bolNeuErstellt = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Die Tabelle ''\" & strMNTabelle & \"'' wurde angelegt.\", vbInformation + vbOKOnly, _\r\n             \"Tabelle erfolgreich angelegt\"\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Die Tabelle ''\" & strMNTabelle & \"'' wurde nicht angelegt.\", vbExclamation + vbOKOnly, _\r\n             \"Tabelle nicht angelegt\"\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: Aufruf der Prozedur zum Anlegen der Verkn&uuml;pfungstabelle<\/span><\/b><\/p>\n<p>Diese schreibt zun&auml;chst den Namen der zu erstellenden Verkn&uuml;pfungstabelle in die Variable <b>strMNTabelle<\/b>. Dann ruft sie die Funktion <b>ExistsTable <\/b>mit dieser Variablen als Parameter auf. Liefert diese Funktion den Wert <b>True<\/b>, dann erscheint eine Meldung mit der Frage, ob die bereits vorhandene Tabelle aus <b>strMNTabelle <\/b>&uuml;berschrieben werden soll (siehe Bild 11).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_027.png\" alt=\"Die zu erstellende Tabelle ist bereits vorhanden.\" width=\"574,559\" height=\"326,7593\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 11: Die zu erstellende Tabelle ist bereits vorhanden.<\/span><\/b><\/p>\n<p>Beantwortet der Benutzer dies mit <b>Ja<\/b>, ruft die Prozedur in einer <b>If&#8230;Then<\/b>-Bedingung mit dem Vergleich des Ergebnisses der Funktion <b>DeleteTable <\/b>und dem Wert <b>True <\/b>auf. Ist das L&ouml;schen der Tabelle erfolgreich, liefert die Funktion den Wert <b>True <\/b>zur&uuml;ck und die Prozedur setzt den Wert der Variablen <b>bolNeuErstellen <\/b>auf <b>True<\/b>. Wenn die weiter oben erw&auml;hnte <b>If&#8230;Then<\/b>-Bedingung mit der Funktion <b>ExistsTable <\/b>den Wert <b>False <\/b>liefert, stellt dies den Wert <b>bolNeuErstellen <\/b>ebenfalls auf <b>True <\/b>ein. Dies geschieht nicht, wenn der Benutzer dem &Uuml;berschreiben nicht zustimmt oder wenn das anschlie&szlig;ende L&ouml;schen der Tabelle fehlschl&auml;gt.<\/p>\n<p>Die folgende <b>If&#8230;Then<\/b>-Bedingung pr&uuml;ft, ob <b>bolNeuErstellen <\/b>den Wert <b>True <\/b>hat und ruft dann als Kriterium einer weiteren <b>If&#8230;Then<\/b>-Bedingung die Funktion <b>AddManyToManyTable <\/b>auf. Dieser &uuml;bergibt sie dann den Namen der zu erstellenden Tabelle aus der Variablen <b>strMNTabelle <\/b>und die &uuml;brigen Parameterwerte aus den Textfeldern <b>txtPrimaerschluesselfeld<\/b>, <b>txtNFremdschluesselfeld <\/b>und <b>txtMFremd-schluesselfeld<\/b>.<\/p>\n<p>War dies erfolgreich, stattet die Prozedur die Variable <b>bolNeuErstellt <\/b>mit dem Wert <b>True <\/b>aus. Diesen pr&uuml;ft die letzte <b>If&#8230;Then<\/b>-Bedingung dieser Prozedur. Ist er <b>True<\/b>, erscheint eine Meldung, dass die neue Verkn&uuml;pfungstabelle erfolgreich angelegt werden konnte, anderenfalls die entsprechende Meldung &uuml;ber das Nichterstellen der Tabelle.<\/p>\n<h2>Ergebnis des Assistenten<\/h2>\n<p>Das Ergebnis sehen Sie dann im ersten Schritt im Navigationsbereich. Hier taucht die neue Tabelle <b>tblKategorienProdukte <\/b>zwischen den beiden anderen Tabellen auf (siehe Bild 12).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_028.png\" alt=\"Die frisch erstellte Tabelle\" width=\"524,559\" height=\"317,3365\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 12: Die frisch erstellte Tabelle<\/span><\/b><\/p>\n<p>Wenn wir die neue Tabelle in der Entwurfsansicht &ouml;ffnen, finden wir das Ergebnis aus Bild 13 vor. Es scheint alles an Ort und Stelle zu sein &#8211; das Prim&auml;rschl&uuml;sselfeld, die beiden Fremdschl&uuml;sselfelder und auch der Prim&auml;rschl&uuml;ssel und der zusammengesetzte eindeutige Schl&uuml;ssel f&uuml;r die beiden Fremdschl&uuml;sselfelder ist bereits vorhanden.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_029.png\" alt=\"Die neue Verkn&uuml;pfungstabelle in der Entwurfsansicht\" width=\"549,559\" height=\"360,0558\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 13: Die neue Verkn&uuml;pfungstabelle in der Entwurfsansicht<\/span><\/b><\/p>\n<p>Wenn wir in die Datenblattansicht der Tabelle wechseln und hier versuchen, mit den Fremdschl&uuml;sselfeldern Werte aus den beiden Tabellen <b>tblProdukte <\/b>und <b>tblKategorien <\/b>auszuw&auml;hlen, sehen wir, dass wir noch keine Nachschlagefelder definiert haben. Aber keine Sorge &#8211; das holen wir noch nach.<\/p>\n<h2>Beziehungen hinzuf&uuml;gen<\/h2>\n<p>Ein Blick in das Beziehungen-Fenster von Access zeigt jedoch, das wir noch nicht fertig sind. Es fehlen n&auml;mlich noch die Beziehungen zwischen den Tabellen (siehe Bild 14).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_030.png\" alt=\"Es fehlen noch die Beziehungen.\" width=\"499,5589\" height=\"207,7204\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 14: Es fehlen noch die Beziehungen.<\/span><\/b><\/p>\n<p>Diese f&uuml;gen wir nun als N&auml;chstes noch hinzu. Dazu erstellen wir als Erstes wieder eine Funktion, die alle Schritte dieser Aufgabe f&uuml;r uns &uuml;bernimmt. Die Funktion hei&szlig;t <b>AddRelation <\/b>und ist in Listing 5 zu finden. Sie erwartet die folgenden Parameter:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>AddRelation(strPKTable<span style=\"color:blue;\"> As String<\/span>, strPKField<span style=\"color:blue;\"> As String<\/span>, strFKTable<span style=\"color:blue;\"> As String<\/span>, strFKField<span style=\"color:blue;\"> As String<\/span>, _\r\n         bolCascadeDelete<span style=\"color:blue;\"> As Boolean<\/span>, bolCascadeUpdate<span style=\"color:blue;\"> As Boolean<\/span>)<span style=\"color:blue;\"> As Boolean<\/span>\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>rel<span style=\"color:blue;\"> As <\/span>DAO.Relation\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>strRelation<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n     strRelation = strPKTable & \"_\" & strFKTable\r\n     <span style=\"color:blue;\">Set<\/span> rel = db.CreateRelation(strRelation, strPKTable, strFKTable)\r\n     <span style=\"color:blue;\">Set<\/span> fld = rel.CreateField(strPKField)\r\n     fld.ForeignName = strFKField\r\n     rel.Fields.Append fld\r\n     <span style=\"color:blue;\">If <\/span>bolCascadeDelete<span style=\"color:blue;\"> Then<\/span>\r\n         rel.Attributes = rel.Attributes + dbRelationDeleteCascade\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">If <\/span>bolCascadeUpdate = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         rel.Attributes = rel.Attributes + dbRelationUpdateCascade\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     db.Relations.Append rel\r\n     <span style=\"color:blue;\">If <\/span>Err.Number = 0<span style=\"color:blue;\"> Then<\/span>\r\n         AddRelation = <span style=\"color:blue;\">True<\/span>\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         Select Case Err.Number\r\n             <span style=\"color:blue;\">Case <\/span>3368\r\n                 <span style=\"color:blue;\">MsgBox<\/span> \"Die an der Beziehung beteiligten Felder m&uuml;ssen den gleichen Datentyp haben. Das ist bei ''\" _\r\n                     & strPKTable & \".\" & strPKField & \"'' und ''\" & strFKTable & \".\" & strFKField _\r\n                     & \"'' nicht der Fall.\", vbOKOnly + vbExclamation, \"Felder inkompatibel\"\r\n             <span style=\"color:blue;\">Case <\/span>0\r\n             <span style=\"color:blue;\">Case Else<\/span>\r\n                 <span style=\"color:blue;\">MsgBox<\/span> Err.Number & <span style=\"color:blue;\">vbCrLf<\/span> & <span style=\"color:blue;\">vbCrLf<\/span> & Err.Description\r\n         <span style=\"color:blue;\">End Select<\/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 5: Anlegen der Beziehung<\/span><\/b><\/p>\n<ul>\n<li><b>strPKTable<\/b>: Die Tabelle mit dem an der Verkn&uuml;pfung beteiligten Prim&auml;rschl&uuml;sselfeld, also die vorhandene m- oder n-Tabelle.<\/li>\n<li><b>strPKField<\/b>: Das an der Verkn&uuml;pfung beteiligte Prim&auml;rschl&uuml;sselfeld.<\/li>\n<li><b>strFKTable<\/b>: Die Tabelle mit dem an der Verkn&uuml;pfung beteiligten Fremdschl&uuml;sselfeld, also die neu erstellte Verkn&uuml;pfungstabelle.<\/li>\n<li><b>strFKField<\/b>: Das Fremdschl&uuml;sselfeld dieser Tabelle.<\/li>\n<li><b>bolCascadeDelete<\/b>: Gibt an, ob die L&ouml;schweitergabe f&uuml;r die Beziehung eingerichtet werden soll.<\/li>\n<li><b>bolCascadeUpdate<\/b>: Gibt an, ob die Aktualisierungsweitergabe eingerichtet werden soll<\/li>\n<\/ul>\n<p>Die Funktion erstellt eine Variable mit einem Verweis auf das aktuelle <b>Database<\/b>-Objekt und setzt dann den Namen f&uuml;r das zu erstellende <b>Relation<\/b>-Objekt aus den durch einen Unterstrich getrennten Namen der an der Beziehung beteiligten Tabellen zusammen. Dann erstellt sie mit <b>CreateRelation <\/b>das neue <b>Relation<\/b>-Objekt und &uuml;bergibt dabei den Namen der zu erstellenden Beziehung sowie die an der Beziehung beteiligten Tabellen.<\/p>\n<p>Dann legt sie das Feld f&uuml;r diese Relation an, das sie zuerst mit der <b>CreateField<\/b>-Methode des <b>Relation<\/b>-Objekts erstellt und dabei den Namen des Prim&auml;rschl&uuml;sselfeldes verwendet. F&uuml;r dieses Feld legt sie dann noch mit der Eigenschaft <b>ForeignName <\/b>das passende Fremdschl&uuml;sselfeld fest. Das so erstellte Feld h&auml;ngt sie dann mit <b>Append <\/b>an die Liste der Felder des <b>Relation<\/b>-Objekts an.<\/p>\n<p>Schlie&szlig;lich folgen noch die Einstellungen f&uuml;r die L&ouml;sch- und Aktualisierungsweitergabe. Wurde mit dem Parameter <b>bolCascadeDelete <\/b>der Wert <b>True <\/b>&uuml;bergeben, werden die Attribute des <b>Relation<\/b>-Objekts um den Wert <b>dbRelationDeleteCascade <\/b>erweitert.<\/p>\n<p>F&uuml;r den Parameter <b>bolCascadeUpdate <\/b>f&uuml;gen wir der <b>Attributes<\/b>-Auflistung den Wert <b>dbRelationUpdateCascade <\/b>an.<\/p>\n<p>Schlie&szlig;lich h&auml;ngt die <b>Append<\/b>-Methode das neue <b>Relation<\/b>-Objekt an die <b>Relations<\/b>-Auflistung der aktuellen Datenbank an. Ist der Fehler mit der Nummer <b>3368 <\/b>aufgetreten, hat der Benutzer Felder mit inkompatiblen Datentypen ausgew&auml;hlt und wir geben eine entsprechende Meldung aus.<\/p>\n<p>Bei allen anderen Feldern setzt die Funktion eine Fehlermeldung aus Fehlernummer und -beschreibung zusammen.<\/p>\n<p>Nun fehlen noch die Aufrufe dieser Funktion f&uuml;r die beiden anzulegenden Beziehungen. Diesen bauen wir wie in Listing 6 in die Prozedur <b>cmdVerknuepfungstabelleAnlegen_Click <\/b>ein.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdVerknuepfungstabelleAnlegen_Click()\r\n     ...\r\n     <span style=\"color:blue;\">If <\/span>bolNeuErstellen = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         If AddManyToManyTable(strMNTabelle, Me!txtPrimaerschluesselfeld, Me!txtNFremdschluesselfeld, _\r\n                 Me!txtMFremdschluesselfeld) = <span style=\"color:blue;\">True<\/span> Then\r\n             If AddRelation(Me!cboMTabelle, Me!cboMPrimaerschluessel, strMNTabelle, Me!txtMFremdschluesselfeld, _\r\n                     Me!chkMLoeschweitergabe, Me!chkMAktualisierungsweitergabe) = <span style=\"color:blue;\">True<\/span> Then\r\n                 If AddRelation(Me!cboNTabelle, Me!cboNPrimaerschluessel, strMNTabelle, Me!txtNFremdschluesselfeld, _\r\n                         Me!chkNLoeschweitergabe, Me!chkNAktualisierungsweitergabe) = <span style=\"color:blue;\">True<\/span> Then\r\n                     bolNeuErstellt = <span style=\"color:blue;\">True<\/span>\r\n                 <span style=\"color:blue;\">End If<\/span>\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 6: Aufrufe der Funktion zum Anlegen der Beziehung<\/span><\/b><\/p>\n<p>So wird die Funktion <b>AddRelation <\/b>nur aufgerufen, wenn die Verkn&uuml;pfungstabelle bereits erfolgreich erstellt wurde. Bei den Aufrufen &uuml;bergeben wir die geforderten Werte f&uuml;r die Parameter, die wir aus den Steuerelementen des Formulars <b>frmMNAssistent <\/b>auslesen. Nur wenn beide Funktionsaufrufe den Wert <b>True <\/b>zur&uuml;ckliefern, stellen wir die Variable <b>bolErstellt <\/b>auf den Wert <b>True <\/b>ein.<\/p>\n<p>Das Ergebnis sehen wir dann in Bild 15. Hier haben wir f&uuml;r die Beziehung zwischen den Tabellen <b>tblKategorien <\/b>und <b>tblKategorienProdukte <\/b>die Option <b>L&ouml;schweitergabe aktivieren <\/b>gew&auml;hlt, aber nicht die zweite Option <b>Aktualisierungsweitergabe aktivieren<\/b>.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_012.png\" alt=\"Per VBA angelegte Beziehung mit L&ouml;schweitergabe\" width=\"599,559\" height=\"378,6688\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 15: Per VBA angelegte Beziehung mit L&ouml;schweitergabe<\/span><\/b><\/p>\n<h2>Nachschlagefelder hinzuf&uuml;gen<\/h2>\n<p>Die Vorbereitungen f&uuml;r das Hinzuf&uuml;gen der Nachschlage-felder haben wir schon getroffen &#8211; es gibt im Formular <b>frmMNAssistent <\/b>f&uuml;r die beiden an der Beziehung beteiligten Tabellen jeweils ein Kontrollk&auml;stchen mit der Beschriftung <b>Nachschlagefeld<\/b>. Auch hier ben&ouml;tigen wir wieder eine Funktion, die wir <b>AddLookupField<\/b> nennen (siehe Listing 7).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>AddLookupField(strFKTable<span style=\"color:blue;\"> As String<\/span>, strFKField<span style=\"color:blue;\"> As String<\/span>, strPKTable<span style=\"color:blue;\"> As String<\/span>, strPKField<span style=\"color:blue;\"> As String<\/span>, _\r\n         strDisplayField<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As Boolean<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>dao.Database\r\n     <span style=\"color:blue;\">Dim <\/span>tdf<span style=\"color:blue;\"> As <\/span>dao.TableDef\r\n     <span style=\"color:blue;\">Dim <\/span>fld<span style=\"color:blue;\"> As <\/span>dao.Field\r\n     <span style=\"color:blue;\">Dim <\/span>prp<span style=\"color:blue;\"> As <\/span>dao.Property\r\n     <span style=\"color:blue;\">Dim <\/span>strRowSource<span style=\"color:blue;\"> As String<\/span>\r\n     On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> tdf = db.TableDefs(strFKTable)\r\n     <span style=\"color:blue;\">Set<\/span> fld = tdf.Fields(strFKField)\r\n     <span style=\"color:blue;\">Set<\/span> prp = fld.CreateProperty(\"DisplayControl\", dbInteger, acComboBox)\r\n     fld.Properties.Append prp\r\n     <span style=\"color:blue;\">Set<\/span> prp = fld.CreateProperty(\"RowSourceType\", dbText, \"Table\/Query\")\r\n     fld.Properties.Append prp\r\n     strRowSource = \"SELECT \" & strPKField & \", \" & strDisplayField & \" FROM \" & strPKTable\r\n     <span style=\"color:blue;\">Set<\/span> prp = fld.CreateProperty(\"RowSource\", dbText, strRowSource)\r\n     fld.Properties.Append prp\r\n     <span style=\"color:blue;\">Set<\/span> prp = fld.CreateProperty(\"ColumnCount\", dbInteger, 2)\r\n     fld.Properties.Append prp\r\n     <span style=\"color:blue;\">Set<\/span> prp = fld.CreateProperty(\"ColumnWidths\", dbText, \"0\")\r\n     fld.Properties.Append prp\r\n     <span style=\"color:blue;\">If <\/span>Err.Number = 0<span style=\"color:blue;\"> Then<\/span>\r\n         AddLookupField = <span style=\"color:blue;\">True<\/span>\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> Err.Number & <span style=\"color:blue;\">vbCrLf<\/span> & <span style=\"color:blue;\">vbCrLf<\/span> & Err.Description\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 7: Anlegen der Lookupfelder<\/span><\/b><\/p>\n<p>Diese Funktion erwartet erwartet die folgenden Parameter:<\/p>\n<ul>\n<li><b>strFKTable<\/b>: Name der Tabelle mit dem zu erstellenden Nachschlagefeld<\/li>\n<li><b>strFKField<\/b>: Name des Feldes, das in ein Nachschlage-feld umgewandelt werden soll<\/li>\n<li><b>strPKTable<\/b>: Name der Tabelle, aus der die Daten f&uuml;r das Nachschlagefeld stammen<\/li>\n<li><b>strPKField<\/b>: Prim&auml;rschl&uuml;ssel der Tabelle, aus der die Daten f&uuml;r das Nachschlagefeld stammen<\/li>\n<li><b>strDisplayField<\/b>: Name des Feldes mit den anzuzeigenden Daten aus der Tabelle, welche die Daten f&uuml;r das Nachschlagefeld liefert<\/li>\n<\/ul>\n<p>Die Funktion f&uuml;llt wieder eine Variable mit einem Verweis auf das aktuelle <b>Database<\/b>-Objekt. Au&szlig;erdem ben&ouml;tigen wir ein <b>TableDef<\/b>-Objekt f&uuml;r die Tabelle, deren Feld wir in ein Nachschlagefeld umwandeln wollen, sowie einen Verweis auf das umzuwandelnde Feld.<\/p>\n<p>Der Verweis darauf landet in der Variablen <b>fld<\/b>.<\/p>\n<p>Im Folgenden nimmt die Funktion &Auml;nderungen an dem mit fld referenzierten Feld vor, und zwar stellt es einige Eigenschaften dieses Feldes ein. Diese sollen die Werte wie in Bild 16 gezeigt erhalten.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_013.png\" alt=\"Diese Werte sollen per VBA angelegt werden.\" width=\"649,559\" height=\"358,0671\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 16: Diese Werte sollen per VBA angelegt werden.<\/span><\/b><\/p>\n<p>Der erste Schritt ist, das anzuzeigende Steuerelement zu &auml;ndern. Das erledigen wir mit der Property <b>DisplayControl <\/b>mit dem Datentyp <b>dbInteger <\/b>und dem Wert <b>acComboBox<\/b>. Diese Informationen &uuml;bergeben wir der Methode <b>CreateProperty <\/b>des Feldes aus <b>fld<\/b>. Anschlie&szlig;end h&auml;ngen wir das neu erstellte <b>Property<\/b>-Element mit <b>Append <\/b>an die Auflistung <b>Properties <\/b>dieses Feldes an.<\/p>\n<p>Danach k&uuml;mmern wir uns um den Herkunftstyp, den wir mit der Eigenschaft <b>RowSourceType <\/b>auf den Wert <b>Table\/Query <\/b>festlegen. Danach stellen wir die Abfrage f&uuml;r die Eigenschaft <b>Datensatzherkunft <\/b>ein, die wir zuvor noch aus den &uuml;bergebenen Parametern zusammenstellen und in der Variablen <b>strRowSource <\/b>speichern. Von dort geben wir die Abfrage weiter an die Eigenschaft <b>RowSource <\/b>mit dem Datentyp <b>dbText<\/b>.<\/p>\n<p>Damit fehlen noch zwei Angaben: die <b>Spaltenanzahl<\/b>, die wir mit der Eigenschaft <b>ColumnCount <\/b>und dem Wert <b>2 <\/b>angeben und die <b>Spaltenbreiten<\/b>, welche mit dem Wert <b>0 <\/b>in der Eigenschaft <b>ColumnWidths <\/b>landen.<\/p>\n<h2>Aufrufe der Funktion zum Anlegen der Nachschlagefelder integrieren<\/h2>\n<p>Auch diese Funktion hilft uns nicht weiter, wenn wir sie nicht aufrufen. Das erledigen wir wieder in der Prozedur <b>cmdVerknuepfungstabelleAnlegen_Click<\/b>, wo wir zwei Aufrufe der Funktion <b>AddLookupField <\/b>integrieren (siehe Listing 8). Der Einbau f&uuml;r das Nachschlagefeld f&uuml;r die Anzeige der Daten aus der m-Tabelle erfolgt hinter der <b>If&#8230;Then<\/b>-Bedingung, in der wir die Verkn&uuml;pfungstabelle und die Beziehungen erstellt haben, und nur, wenn dies erfolgreich war und wenn der Benutzer die entsprechende Option mit dem Kontrollk&auml;stchen <b>chkMNachschlagefeld <\/b>ausgew&auml;hlt hat.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdVerknuepfungstabelleAnlegen_Click()\r\n     ...\r\n     <span style=\"color:blue;\">If <\/span>bolNeuErstellt = <span style=\"color:blue;\">True<\/span> And Me!chkMNachschlagefeld = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         bolNeuErstellt = <span style=\"color:blue;\">False<\/span>\r\n         If AddLookupField(Me!txtVerknuepfungstabelle, Me!txtMFremdschluesselfeld, Me!cboMTabelle, _\r\n                 Me!cboMPrimaerschluessel, Me!cboMNachschlagefeld) = <span style=\"color:blue;\">True<\/span> Then\r\n             bolNeuErstellt = <span style=\"color:blue;\">True<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">If <\/span>bolNeuErstellt = <span style=\"color:blue;\">True<\/span> And Me!chkNNachschlagefeld = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         bolNeuErstellt = <span style=\"color:blue;\">False<\/span>\r\n         If AddLookupField(Me!txtVerknuepfungstabelle, Me!txtNFremdschluesselfeld, Me!cboNTabelle, _\r\n                 Me!cboNPrimaerschluessel, Me!cboNNachschlagefeld) = <span style=\"color:blue;\">True<\/span> Then\r\n             bolNeuErstellt = <span style=\"color:blue;\">True<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">If <\/span>bolNeuErstellt = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         ...\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 8: Aufruf der Funktion zum Anlegen der Lookupfelder<\/span><\/b><\/p>\n<p>Ist das der Fall, setzen wir <b>bolNeuErstellt <\/b>zur&uuml;ck auf <b>False <\/b>und f&uuml;llen diese Variable mit dem Ergebnis des Aufrufs der Funktion <b>AddLookupField<\/b>. Dies erledigen wir anschlie&szlig;end direkt noch f&uuml;r das Nachschlagefeld f&uuml;r die n-Tabelle, sofern dies vom Benutzer so selektiert wurde.<\/p>\n<h2>Umwandeln in ein Access-Add-In<\/h2>\n<p>Damit haben wir alle Aufgaben zum Erstellen von m:n-Verkn&uuml;pfungstabellen erledigt und brauchen die Datenbank nur noch in ein Access-Add-In umzuwandeln. Dazu sind die folgenden Schritte n&ouml;tig:<\/p>\n<ul>\n<li>&Auml;ndern der Dateiendung von <b>.accdb <\/b>in <b>.accda<\/b><\/li>\n<li>Hinzuf&uuml;gen einer Tabelle namens <b>USysRegInfo<\/b><\/li>\n<li>Ereignis anlegen, dass beim Aufrufen des Access-Add-Ins ausgel&ouml;st wird<\/li>\n<li>Einstellen der Eigenschaften, die im Add-In-Manager erscheinen<\/li>\n<li>Installieren des Access-Add-Ins<\/li>\n<li>Anpassen der Anwendung, falls nicht alles im geladenen Add-In so l&auml;uft, wie wir es in der <b>.accdb<\/b>-Datenbank programmiert haben<\/li>\n<\/ul>\n<p>Das &Auml;ndern der Dateiendung brauchen wir nicht weiter zu erl&auml;utern, den Rest finden Sie in den folgenden Abschnitten.<\/p>\n<h2>Hinzuf&uuml;gen der Tabelle <b>USysRegInfo<\/b><\/h2>\n<p>Die Tabelle <b>USysRegInfo <\/b>wird beim Installieren eines Add-Ins mit dem Add-In-Manager ausgelesen, um die Inhalte in die Registry zu schreiben.<\/p>\n<p>Wenn Sie an der richtigen Stelle landen, liest Access diese jeweils beim Start (und auch direkt nach der Installation) aus der Registry aus und zeigt die dort aufgelisteten Add-Ins an den entsprechenden Stellen der Entwicklungsumgebung an. Diese Tabelle sieht f&uuml;r unseren m:n-Assistenten wie in Bild 17 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_0014.png\" alt=\"Die Tabelle USysRegInfo\" width=\"700\" height=\"287,0257\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 17: Die Tabelle USysRegInfo<\/span><\/b><\/p>\n<p>Der letzte Datensatz mit dem Wert <b>0 <\/b>im Feld <b>Type <\/b>sorgt daf&uuml;r, dass der Eintrag in der Registry &uuml;berhaupt angelegt wird.<\/p>\n<p>Der Datensatz mit dem Wert <b>Expression <\/b>im Feld <b>ValName <\/b>gibt an, welche VBA-Funktion beim Aufrufen des Add-Ins gestartet werden soll. Der Datensatz mit dem Wert <b>Library <\/b>im Feld <b>ValName <\/b>liefert den Namen der Datenbankdatei, die als Add-In dienen soll.<\/p>\n<p>Hier verwenden wir den Ausdruck <b>|ACCDIR <\/b>als Platzhalter f&uuml;r das Add-In-Verzeichnis, dass sich in der Regel im Verzeichnis <b>C:\\Users\\<Benutzername>\\AppData\\Roaming\\Microsoft\\AddIns <\/b>befindet. Der Datensatz mit dem Wert <b>Description <\/b>im Feld <b>ValName <\/b>liefert eine Beschreibung des Access-Add-Ins.<\/p>\n<h2>Funktion f&uuml;r den Add-In-Start anlegen<\/h2>\n<p>Die Funktion, die wir in der Tabelle <b>USysRegInfo <\/b>angegeben haben und die beim Aufrufen des Add-Ins ausgel&ouml;st werden soll, legen wir in einem Modul namens <b>mdlAddIn <\/b>an. Sie soll nur das Formular <b>frmMNAssistent<\/b> &ouml;ffnen und sieht wie folgt aus:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>Autostart()\r\n     DoCmd.OpenForm \"frmMNAssistent\"\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<h2>Einstellen der Eigenschaften f&uuml;r den Add-In-Manager<\/h2>\n<p>Damit der Eintrag f&uuml;r das Add-In nach der Auswahl im Add-In-Manager korrekt angezeigt wird, f&uuml;gen wir der Datenbank einige Eigenschaften hinzu.<\/p>\n<p>Um den <b>Eigenschaften<\/b>-Dialog f&uuml;r die Datenbank zu &ouml;ffnen, wechseln Sie im Ribbon zum Bereich <b>Datei<\/b>. Hier finden Sie den Bereich <b>Informationen<\/b>, der einen Link namens <b>Datenbankeigenschaften anzeigen und bearbeiten<\/b> enth&auml;lt.<\/p>\n<p>Wenn Sie diesen Link bet&auml;tigen, k&ouml;nnen Sie die Eigenschaften wie in Bild 18 anpassen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_0013.png\" alt=\"Datenbankeigenschaften, die im Add-In-Manager angezeigt werden\" width=\"424,5589\" height=\"458,6841\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 18: Datenbankeigenschaften, die im Add-In-Manager angezeigt werden<\/span><\/b><\/p>\n<h2>Installieren des Access-Add-Ins<\/h2>\n<p>Danach k&ouml;nnen wir erstmalig das Access-Add-In installieren. Dazu bet&auml;tigen Sie den Ribbon-Eintrag <b>Datenbanktools|Add-Ins|Add-Ins|Add-In-Manager<\/b>. Dies &ouml;ffnet den Dialog <b>Add-In-Manager<\/b>, in dem Sie auf die Schaltfl&auml;che <b>Neues hinzuf&uuml;gen&#8230; <\/b>klicken. Sie w&auml;hlen dann im <b>&Ouml;ffnen<\/b>-Dialog die <b>.accda<\/b>-Datenbank aus und best&auml;tigen die Auswahl.<\/p>\n<p>Danach finden Sie den neuen Eintrag im <b>Add-In-Manager <\/b>vor (siehe Bild 19). <\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_031.png\" alt=\"Das neue Add-In im Add-In-Manager\" width=\"424,5589\" height=\"253,7593\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 19: Das neue Add-In im Add-In-Manager<\/span><\/b><\/p>\n<p>Wenn wir danach die Liste der Add-Ins &uuml;ber den Ribbon-Eintrag <b>Datenbanktools|Add-Ins|Add-Ins <\/b>&ouml;ffnen, finden wir dort ebenfalls den Eintrag <b>amvMNAssistent <\/b>vor. Wenn Sie nun eine andere Anwendung &ouml;ffnen, k&ouml;nnen Sie das Add-In nun das erste Mal von einer anderen Anwendung aus starten (siehe Bild 20).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_033.png\" alt=\"Das Add-In in der Add-Ins-Liste\" width=\"574,559\" height=\"476,8333\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 20: Das Add-In in der Add-Ins-Liste<\/span><\/b><\/p>\n<h2>Anpassen der Anwendung als Add-In<\/h2>\n<p>Der erste Test offenbart dann meist einige notwendige Nachbesserungen, weil wir das Add-In ja bisher komplett als eigenst&auml;ndige Datenbank entwickelt und getestet haben.<\/p>\n<p>Wenn man die Datenbank dann allerdings als Add-In aufruft, w&auml;hrend eigentlich eine andere Datenbank ge&ouml;ffnet ist, kann es sein, dass die eine oder andere Funktion nicht so arbeitet wie geplant.<\/p>\n<p>Wir haben beispielsweise eine kleine Beispieldatenbank erstellt, welche zwei per m:n-Beziehung zu verkn&uuml;pfende Tabellen enth&auml;lt. Beim Versuch, diese mit dem Formular <b>frmMNAssistent <\/b>des Add-Ins auszuw&auml;hlen, tauchen jedoch ganz andere Tabellen auf &#8211; und zwar die Tabellen, die wir zum Testen in der Add-In-Datenbank selbst angelegt hatten (siehe Bild 21).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_034.png\" alt=\"Das Add-In zeigt die falschen Tabellen an.\" width=\"649,559\" height=\"448,3741\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 21: Das Add-In zeigt die falschen Tabellen an.<\/span><\/b><\/p>\n<p>Die beiden Kombinationsfelder zur Auswahl der Tabellen hatten wir mit einer Datensatzherkunft ausgestattet, welche die Tabelle <b>MSysObjects <\/b>verwendet.<\/p>\n<p>Die Add-In-Datenbank sucht dann aber in der eigenen Datenbank nach dieser Tabelle und nicht in er Host-Datenbank, also der Datenbank, von der aus das Add-In gestartet wird.<\/p>\n<p>Das bedeutet also, dass wir einen anderen Weg finden m&uuml;ssen, um die Tabellen der Host-Datenbank anzuzeigen. Da die Menge der Tabellen in der Regel &uuml;berschaubar ist, stellen wir diese per Code zusammen.<\/p>\n<p>Dazu erstellen wir eine Prozedur namens <b>TabellenInKombinationsfeld<\/b>, die wir f&uuml;r jedes der beiden Kombinationsfelder <b>cboMTabelle <\/b>und <b>cboNTabelle <\/b>je einmal aufrufen.<\/p>\n<p>Den Aufruf platzieren wir in der Ereignisprozedur, die durch das Ereignis <b>Beim Laden <\/b>ausgel&ouml;st wird:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     TabellenInKombinationsfeld Me!cboMTabelle\r\n     TabellenInKombinationsfeld Me!cboNTabelle\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die Prozedur <b>TabellenInKombinationsfeld <\/b>erwartet mit dem Parameter <b>cbo <\/b>einen Verweis auf ein <b>ComboBox<\/b>-Steuerelement (siehe Listing 9).<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>TabellenInKombinationsfeld(cbo<span style=\"color:blue;\"> As <\/span>ComboBox)\r\n     <span style=\"color:blue;\">Dim <\/span>objTable<span style=\"color:blue;\"> As Object<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strTable<span style=\"color:blue;\"> As String<\/span>\r\n     cbo.RowSourceType = \"Value List\"\r\n     <span style=\"color:blue;\">Do While<\/span> cbo.ListCount &gt; 0\r\n         cbo.RemoveItem 0\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     cbo.AddItem \"&lt;Ausw&auml;hlen&gt;\"\r\n     For Each objTable In CurrentData.AllTables\r\n         strTable = objTable.Name\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> strTable Like \"MSys*\" And <span style=\"color:blue;\">Not<\/span> strTable Like \"USys*\"<span style=\"color:blue;\"> Then<\/span>\r\n             cbo.AddItem strTable\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Next<\/span> objTable\r\n     For Each objTable In CurrentData.AllQueries\r\n         strTable = objTable.Name\r\n         cbo.AddItem strTable\r\n     <span style=\"color:blue;\">Next<\/span> objTable\r\n     cbo = cbo.ItemData(0)\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 9: Prozedur zum F&uuml;llen eines Kombinationsfeldes mit den Tabellen der Host-Datenbank<\/span><\/b><\/p>\n<p>Die Prozedur stellt als Erstes die Eigenschaft <b>Herkunftsart <\/b>(<b>RowSourceType<\/b>) auf <b>Wertliste <\/b>(<b>Value List<\/b>) ein. Dann pr&uuml;ft sie, ob noch Eintr&auml;ge in den Kombinationsfeldern vorhanden sind und entfernt diese in einer Schleife, die solange durchlaufen wird, bis keine Eintr&auml;ge mehr enthalten sind (<b>cbo.ListCount > 0<\/b>).<\/p>\n<p>Als ersten Eintrag legt die Prozedur einen mit dem Wert <b><Ausw&auml;hlen> <\/b>an. Danach durchl&auml;uft sie eine <b>For Each<\/b>-Schleife &uuml;ber alle Elemente der Auflistung <b>AllTables <\/b>des Objekts <b>CurrentData<\/b>. Das Objekt <b>CurrentData <\/b>bezieht sich immer auf die Daten der aktuell ge&ouml;ffneten Datenbank und nicht auf die der Add-In-Datenbank. Wenn Sie auf die entsprechenden Auflistungen der Add-In-Datenbank zugreifen wollten, k&ouml;nnten Sie das Objekt <b>CodeData <\/b>verwenden (analog zu <b>CodeDb <\/b>und <b>CurrentDb<\/b>).<\/p>\n<p>Mit der <b>AllTables<\/b>-Auflistung greifen wir auf alle in der Host-Datenbank gespeicherten Tabellen zu, also auch auf die Systemtabellen.<\/p>\n<p>Innerhalb der <b>For Each<\/b>-Schleife weisen wir der Variablen <b>strTable <\/b>daher zun&auml;chst den Wert der Eigenschaft <b>objTable.Name <\/b>zu, um diesen anschlie&szlig;end zu untersuchen. Dabei pr&uuml;fen wir, ob die Tabelle mit der Zeichenkette <b>MSys <\/b>oder <b>USys <\/b>beginnt. Nur wenn das nicht der Fall ist, f&uuml;gen wir den Namen der Tabelle aus <b>strTable <\/b>mit der <b>AddItem<\/b>-Methode an das Kombinationsfeld an.<\/p>\n<p>Schlie&szlig;lich stellt die Prozedur das jeweilige Kombinationsfeld noch auf das erste enthalten Element ein, sodass diese jeweils den Eintrag <b><Ausw&auml;hlen> <\/b>anzeigen. Ein Test zeigt, dass die Kombinationsfelder nun die Tabellen der Host-Anwendung anzeigen (siehe Bild 22).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2021_06\/pic_1335_035.png\" alt=\"Diesmal erscheinen die richtigen Tabellen.\" width=\"574,559\" height=\"371,8254\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 22: Diesmal erscheinen die richtigen Tabellen.<\/span><\/b><\/p>\n<p>Jetzt stellt sich allerdings die Frage, ob die Kombinationsfelder zur Auswahl der an der Beziehung beteiligten Felder  funktionieren. Die Kombinationsfelder haben den Datentyp <b>Feldliste <\/b>und nach der Auswahl der Tabelle stellen wir den Namen der jeweiligen Tabelle als <b>Datensatzherkunft <\/b>ein. In diesem Fall sucht Access offensichtlich die Daten direkt in den Tabellen der Host-Datenbank &#8211; hier ist also nichts weiter zu tun. Genauso funktioniert auch der &uuml;brige Code wie gew&uuml;nscht.<\/p>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Dieser Beitrag beschreibt die Erstellung eines etwas aufwendigeren Add-Ins. Dieses erlaubt die weitgehend automatische Erstellung einer Verkn&uuml;pfungstabelle zum Realisieren einer m:n-Beziehung. Dabei braucht der Benutzer nur noch das Add-In zu starten und einige Informationen mit Kombinationsfeldern auszuw&auml;hlen. Dabei kann er einstellen, welche beiden Tabellen an der m:n-Beziehung beteiligt sind, welche Prim&auml;rschl&uuml;sselfelder genutzt werden und ob die Verkn&uuml;pfungstabelle Nachschlagefelder nutzen soll.<\/p>\n<p>F&uuml;r den letzteren Fall m&uuml;ssen die Felder ausgew&auml;hlt werden, die in den Nachschlagefeldern angezeigt werden. Au&szlig;erdem kann der Benutzer noch separat angeben, ob f&uuml;r die beiden Beziehungen die L&ouml;schweitergabe und\/oder die Aktualisierungsweitergabe aktiviert werden sollen.<\/p>\n<p>Zum Erstellen der Verkn&uuml;pfungstabelle fragt das Add-In ab, wie die Verkn&uuml;pfungstabelle hei&szlig;en soll sowie das Prim&auml;rschl&uuml;sselfeld und die beiden Fremdschl&uuml;sselfelder. Diese werden jedoch automatisch auf Basis der zu verkn&uuml;pfenden Tabellen vorbelegt, sodass der Benutzer diese &uuml;bernehmen oder gegebenenfalls noch anpassen kann.<\/p>\n<h2>Downloads zu diesem Beitrag<\/h2>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>amvMNAssistent.accda<\/p>\n<p>Beispiel.accdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/F81809C3-AFD3-4801-BADC-697638086E39\/aiu_1335.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Microsoft Access bietet eine ganze Reihe von praktischen Assistenten. Ich setze beispielsweise sehr oft den Nachschlage-Assistenten ein, der nicht nur eine Beziehung mit den gew&uuml;nschten Optionen anlegt, sondern das bearbeitete Feld auch noch als Kombinationsfeld auslegt, mit dem die Daten der verkn&uuml;pften Tabelle leicht ausgew&auml;hlt werden k&ouml;nnen. Eine m:n-Beziehung stellen Sie her, indem Sie zwei solcher Nachschlagefelder in der sogenannten Verkn&uuml;pfungstabelle anlegen. Noch praktischer w&auml;re es, wenn Sie diese Verkn&uuml;pfungstabelle gar nicht erst anlegen m&uuml;ssten. Stattdessen w&auml;ren nur die zu verkn&uuml;pfenden Tabellen auszuw&auml;hlen und der Assistent erledigt den Rest &#8211; das Anlegen der Verkn&uuml;pfungstabelle mit den notwendigen Feldern sowie das Einrichten der Beziehungen zu den zu verkn&uuml;pfenden Tabellen. Dieser Beitrag zeigt, wie Sie einen solchen Assistenten programmieren k&ouml;nnen.<\/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":[662021,66062021,44000027],"tags":[],"class_list":["post-55001335","post","type-post","status-publish","format-standard","hentry","category-662021","category-66062021","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>Assistent f&uuml;r m:n-Beziehungen - 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\/Assistent_fuer_mnBeziehungen\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Assistent f&uuml;r m:n-Beziehungen\" \/>\n<meta property=\"og:description\" content=\"Microsoft Access bietet eine ganze Reihe von praktischen Assistenten. Ich setze beispielsweise sehr oft den Nachschlage-Assistenten ein, der nicht nur eine Beziehung mit den gew&uuml;nschten Optionen anlegt, sondern das bearbeitete Feld auch noch als Kombinationsfeld auslegt, mit dem die Daten der verkn&uuml;pften Tabelle leicht ausgew&auml;hlt werden k&ouml;nnen. Eine m:n-Beziehung stellen Sie her, indem Sie zwei solcher Nachschlagefelder in der sogenannten Verkn&uuml;pfungstabelle anlegen. Noch praktischer w&auml;re es, wenn Sie diese Verkn&uuml;pfungstabelle gar nicht erst anlegen m&uuml;ssten. Stattdessen w&auml;ren nur die zu verkn&uuml;pfenden Tabellen auszuw&auml;hlen und der Assistent erledigt den Rest - das Anlegen der Verkn&uuml;pfungstabelle mit den notwendigen Feldern sowie das Einrichten der Beziehungen zu den zu verkn&uuml;pfenden Tabellen. Dieser Beitrag zeigt, wie Sie einen solchen Assistenten programmieren k&ouml;nnen.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Assistent_fuer_mnBeziehungen\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2021-12-10T07:39:55+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg07.met.vgwort.de\/na\/4281bdcab75f4a3ea714d77a8d68b1c8\" \/>\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=\"35\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Assistent_fuer_mnBeziehungen\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Assistent_fuer_mnBeziehungen\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Assistent f&uuml;r m:n-Beziehungen\",\"datePublished\":\"2021-12-10T07:39:55+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Assistent_fuer_mnBeziehungen\\\/\"},\"wordCount\":5831,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Assistent_fuer_mnBeziehungen\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg07.met.vgwort.de\\\/na\\\/4281bdcab75f4a3ea714d77a8d68b1c8\",\"articleSection\":[\"2021\",\"6\\\/2021\",\"L\u00f6sungen\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Assistent_fuer_mnBeziehungen\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Assistent_fuer_mnBeziehungen\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Assistent_fuer_mnBeziehungen\\\/\",\"name\":\"Assistent f&uuml;r m:n-Beziehungen - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Assistent_fuer_mnBeziehungen\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Assistent_fuer_mnBeziehungen\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg07.met.vgwort.de\\\/na\\\/4281bdcab75f4a3ea714d77a8d68b1c8\",\"datePublished\":\"2021-12-10T07:39:55+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Assistent_fuer_mnBeziehungen\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Assistent_fuer_mnBeziehungen\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Assistent_fuer_mnBeziehungen\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg07.met.vgwort.de\\\/na\\\/4281bdcab75f4a3ea714d77a8d68b1c8\",\"contentUrl\":\"http:\\\/\\\/vg07.met.vgwort.de\\\/na\\\/4281bdcab75f4a3ea714d77a8d68b1c8\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Assistent_fuer_mnBeziehungen\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Assistent f&uuml;r m:n-Beziehungen\"}]},{\"@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":"Assistent f&uuml;r m:n-Beziehungen - 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\/Assistent_fuer_mnBeziehungen\/","og_locale":"de_DE","og_type":"article","og_title":"Assistent f&uuml;r m:n-Beziehungen","og_description":"Microsoft Access bietet eine ganze Reihe von praktischen Assistenten. Ich setze beispielsweise sehr oft den Nachschlage-Assistenten ein, der nicht nur eine Beziehung mit den gew&uuml;nschten Optionen anlegt, sondern das bearbeitete Feld auch noch als Kombinationsfeld auslegt, mit dem die Daten der verkn&uuml;pften Tabelle leicht ausgew&auml;hlt werden k&ouml;nnen. Eine m:n-Beziehung stellen Sie her, indem Sie zwei solcher Nachschlagefelder in der sogenannten Verkn&uuml;pfungstabelle anlegen. Noch praktischer w&auml;re es, wenn Sie diese Verkn&uuml;pfungstabelle gar nicht erst anlegen m&uuml;ssten. Stattdessen w&auml;ren nur die zu verkn&uuml;pfenden Tabellen auszuw&auml;hlen und der Assistent erledigt den Rest - das Anlegen der Verkn&uuml;pfungstabelle mit den notwendigen Feldern sowie das Einrichten der Beziehungen zu den zu verkn&uuml;pfenden Tabellen. Dieser Beitrag zeigt, wie Sie einen solchen Assistenten programmieren k&ouml;nnen.","og_url":"https:\/\/access-im-unternehmen.de\/Assistent_fuer_mnBeziehungen\/","og_site_name":"Access im Unternehmen","article_published_time":"2021-12-10T07:39:55+00:00","og_image":[{"url":"http:\/\/vg07.met.vgwort.de\/na\/4281bdcab75f4a3ea714d77a8d68b1c8","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"35\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/Assistent_fuer_mnBeziehungen\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Assistent_fuer_mnBeziehungen\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Assistent f&uuml;r m:n-Beziehungen","datePublished":"2021-12-10T07:39:55+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Assistent_fuer_mnBeziehungen\/"},"wordCount":5831,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Assistent_fuer_mnBeziehungen\/#primaryimage"},"thumbnailUrl":"http:\/\/vg07.met.vgwort.de\/na\/4281bdcab75f4a3ea714d77a8d68b1c8","articleSection":["2021","6\/2021","L\u00f6sungen"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Assistent_fuer_mnBeziehungen\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Assistent_fuer_mnBeziehungen\/","url":"https:\/\/access-im-unternehmen.de\/Assistent_fuer_mnBeziehungen\/","name":"Assistent f&uuml;r m:n-Beziehungen - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Assistent_fuer_mnBeziehungen\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Assistent_fuer_mnBeziehungen\/#primaryimage"},"thumbnailUrl":"http:\/\/vg07.met.vgwort.de\/na\/4281bdcab75f4a3ea714d77a8d68b1c8","datePublished":"2021-12-10T07:39:55+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Assistent_fuer_mnBeziehungen\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Assistent_fuer_mnBeziehungen\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/Assistent_fuer_mnBeziehungen\/#primaryimage","url":"http:\/\/vg07.met.vgwort.de\/na\/4281bdcab75f4a3ea714d77a8d68b1c8","contentUrl":"http:\/\/vg07.met.vgwort.de\/na\/4281bdcab75f4a3ea714d77a8d68b1c8"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Assistent_fuer_mnBeziehungen\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Assistent f&uuml;r m:n-Beziehungen"}]},{"@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\/55001335","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=55001335"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001335\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001335"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001335"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001335"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}