{"id":55001510,"date":"2024-08-01T00:00:00","date_gmt":"2024-08-01T09:54:27","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1510"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"mnBeziehung_mit_Listenfeld_und_Datenblatt","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/mnBeziehung_mit_Listenfeld_und_Datenblatt\/","title":{"rendered":"m:n-Beziehung mit Listenfeld und Datenblatt"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg02.met.vgwort.de\/na\/099a42723ed64bb19d9b294e6a753455\" width=\"1\" height=\"1\" alt=\"\"><b>Es gibt verschiedene M&ouml;glichkeiten, eine m:n-Beziehung zwischen zwei Tabellen in Formularen abzubilden: Mit zwei Listenfeldern, mit einem Haupt- und einem Unterformular und viele weitere. In diesem Beitrag schauen wir uns eine Kombination aus Listenfeld und Datenblatt an. Dabei betrachten wir das Beispiel von Fahrzeugen und Ausstattungsmerkmalen. Eigentlich sollte man meinen, das w&auml;re eine reine m:n-Beziehung, in der die Verkn&uuml;pfungstabelle nur die Zuordnung der Merkmale zu den Fahrzeugen vornimmt. Allerdings liefert einer unserer Kunden ein Beispiel, bei dem es etwas aufwendiger wird: Zus&auml;tzlich zur reinen Zuordnung soll auch noch festgelegt werden k&ouml;nnen, welche Ausstattungsmerkmale mit auf das Preisschild sollen und welche Serien- und welche Sonderausstattungen es gibt. Kein Problem: Dann bauen wir einfach eine ergonomische L&ouml;sung f&uuml;r diesen Fall, wie dieser Beitrag zeigt.<\/b><\/p>\n<p>Man k&ouml;nnte annehmen, dass die Zuordnung von Ausstattungen zu Fahrzeugen &uuml;ber eine einfache m:n-Verkn&uuml;pfungstabelle zu realisieren ist, welche jeweils ein Fremdschl&uuml;sselfeld zum Herstellen einer 1:n-Beziehung zur Tabelle der Fahrzeuge enth&auml;lt und eines zum Herstellen einer 1:n-Beziehung zur Tabelle der Ausstattungsmerkmale. Doch einer unserer Kunden hatte eine Anforderung, die dar&uuml;ber hinausgeht: Er m&ouml;chte nicht nur festlegen, ob ein Ausstattungsmerkmal zu einem Fahrzeug geh&ouml;rt oder nicht, sondern auch noch angeben k&ouml;nnen, ob dieses auf dem Preisschild gelangen soll.<\/p>\n<p>Falls Sie sich fragen, warum man nicht einfach alle Ausstattungen dort unterbringt: Manchmal hat ein Fahrzeug einfach so viele Ausstattungsmerkmale, dass diese nicht alle auf das Preisschild passen, dass im Fahrzeug hinter die Windschutzscheibe gelegt wird. Dann hei&szlig;t es aussortieren! Bisher hat der Kunde dies manuell erledigt.<\/p>\n<p>Das hei&szlig;t, er hat auf Basis der m:n-Beziehung zwischen den Fahrzeugen  und den Ausstattungen eine Komma-separierte Liste generiert, die er zun&auml;chst in einem Memofeld gespeichert hat. Dann hat er diesen Text in ein zweites Memofeld kopiert und die Merkmale, die am wenigsten Kaufanreiz bieten oder so selbstverst&auml;ndlich sind, dass sie nicht aufgef&uuml;hrt werden m&uuml;ssen, aus der zweiten Liste entfernt &#8211; solange, bis diese auf das Preisschild passte.<\/p>\n<p>Das ist allerdings etwas umst&auml;ndlich und auch fehleranf&auml;llig:<\/p>\n<ul>\n<li>Erstens musste er, wenn er einmal versehentlich ein Ausstattungsmerkmal hinzugef&uuml;gt hat, das nicht zum Fahrzeug geh&ouml;rt oder wenn er eine Ausstattung nachtr&auml;glich hinzuf&uuml;gen wollte, alle Texte &uuml;berarbeiten.<\/li>\n<li>Zweitens ist es grunds&auml;tzlich immer anzustreben, so wenig wie m&ouml;glich von Hand Daten nachtr&auml;glich zu bearbeiten.<\/li>\n<\/ul>\n<p>Also mussten wir uns zwei Dinge &uuml;berlegen:<\/p>\n<ul>\n<li>Wie k&ouml;nnen wir die W&uuml;nsche des Kunden im Datenmodell abbilden?<\/li>\n<li>Und wie setzen wir diese &Auml;nderungen in der Benutzeroberfl&auml;che um, sodass die Eingabe so intuitiv und einfach wie m&ouml;glich ist &#8211; und m&ouml;glichst wenig Nachbearbeitung erfordert?<\/li>\n<\/ul>\n<h2>Aktuelles Datenmodell<\/h2>\n<p>Bevor wir starten, schauen wir uns das aktuelle Datenmodell an. Eigentlich hatte der Kunde die Fahrzeuge und Ausstattungen in jeweils einer Tabelle gespeichert und die Zuordnung der Ausstattungen zu den Fahrzeugen &uuml;ber eine 1:n-Beziehung mit Mehrfachauswahl realisiert. Dies haben wir in einem ersten Schritt, den wir im Beitrag <b>m:n-Daten wie im mehrwertigen Feld selektieren <\/b>(<b>www.access-im-unternehmen.de\/1424<\/b>) erl&auml;utern, in eine echte m:n-Beziehung umgewandelt.<\/p>\n<p>Die Ausgangssituation sieht also wie in Bild 1 aus. Wir sehen zwei Tabellen namens <b>tblFahrzeuge <\/b>und <b>tblAusstattungen<\/b>. Diese werden &uuml;ber die Tabelle <b>tblFahrzeugeAusstattungen <\/b>&uuml;ber jeweils ein Fremdschl&uuml;sselfeld verbunden. <\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_001.png\" alt=\"Ausgangsdatenmodell\" width=\"649,559\" height=\"215,4935\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Ausgangsdatenmodell<\/span><\/b><\/p>\n<p>Wir haben au&szlig;erdem einige Beispieldatens&auml;tze hinzugef&uuml;gt. Diese sehen in den Tabellen in der Datenblattansicht wie in Bild 2 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_002.png\" alt=\"Beispieldaten und Zuordnung &uuml;ber die m:n-Verkn&uuml;pfungstabelle\" width=\"700\" height=\"215,047\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Beispieldaten und Zuordnung &uuml;ber die m:n-Verkn&uuml;pfungstabelle<\/span><\/b><\/p>\n<h2>Anpassung des Datenmodells<\/h2>\n<p>Der erste Teil der Aufgabe besteht darin, folgende Informationen in das Datenmodell aufzunehmen:<\/p>\n<ul>\n<li>Soll eine Ausstattung auf dem Preisschild angezeigt werden?<\/li>\n<li>Wenn sie angezeigt wird, soll sie unter der Kategorie Serienausstattung oder Sonderausstattung erscheinen?<\/li>\n<\/ul>\n<p>Wenn wir davon ausgehen, dass es nur die beiden Kategorien Serienausstattung oder Sonderausstattungen gibt, k&ouml;nnten wir in einem ersten Schnellschuss zwei <b>Ja\/Nein<\/b>-Felder in der Tabelle <b>tblFahrzeugeAusstattungen <\/b>unterbringen, die beispielsweise die Namen <b>Serienausstattung <\/b>und <b>Sonderausstattung <\/b>tragen.<\/p>\n<p>Aber meistens kommen dem Kunden im Verlauf immer noch weitere Kunden, was sich mit der neuen Funktion noch so alles anstellen l&auml;sst &#8211; in diesem Fall vielleicht noch eine weitere Kategorie. Au&szlig;erdem hat die aktuelle Idee einen Haken: Wir k&ouml;nnen theoretisch festlegen, dass ein Ausstattungsmerkmal gleichzeitig zur Serienausstattung und zur Sonderausstattung geh&ouml;rt, indem wir beide <b>Ja\/Nein<\/b>-Felder anhaken (siehe Bild 3).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_003.png\" alt=\"Erster Entwurf f&uuml;r die zus&auml;tzlichen Informationen\" width=\"499,5589\" height=\"360,2829\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Erster Entwurf f&uuml;r die zus&auml;tzlichen Informationen<\/span><\/b><\/p>\n<p>Wir m&uuml;ssten schlie&szlig;lich bereits im Formular verhindern, dass beide Felder angekreuzt werden.<\/p>\n<p>Zusammengefasst mit der Idee, dass neben Serienausstattung oder Sonderausstattung noch eine weitere Kategorie hinzukommen k&ouml;nnte, die im Bericht f&uuml;r das Preisschild aufgef&uuml;hrt werden soll, sollten wir diesen ersten Entwurf also verwerfen.<\/p>\n<p>Die n&auml;chste Frage ist: Wird ein Ausstattungsmerkmal in mehr als einer Kategorie auf dem Preisschild erscheinen? Das h&ouml;rt sich wenig sinnvoll an. Ausgehend davon, dass eine Ausstattung nun entweder<\/p>\n<ul>\n<li>nicht auf dem Preisschild erscheint oder<\/li>\n<li>unter einer Kategorie wie Serienausstattung, Sonderausstattung oder einer anderen Kategorie erscheint,<\/li>\n<\/ul>\n<p>k&ouml;nnen wir den folgenden Entwurf ins Auge fassen: Wir erstellen eine neue Tabelle namens <b>tblAusstattungskategorien <\/b>mit den Feldern <b>AusstattungskategorieID <\/b>und <b>Ausstattungskategorie <\/b>und mit Werten wie <b>Serienausstattung <\/b>oder <b>Sonderausstattung<\/b>. Diese sieht in der Entwurfsansicht wie in Bild 4 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_004.png\" alt=\"Entwurf der Tabelle tblAusstattungskategorien\" width=\"549,559\" height=\"355,8547\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Entwurf der Tabelle tblAusstattungskategorien<\/span><\/b><\/p>\n<p>Der m:n-Verkn&uuml;pfungstabelle <b>tblFahrzeugeAusstattungen <\/b>f&uuml;gen wir ein neues Feld namens <b>PreisschildAusstattungskategorieID<\/b> hinzu. Warum nennen wir dieses Feld nicht einfach <b>AusstattungskategorieID<\/b>? Weil wir flexibel bleiben wollen und es vorkommen kann, dass der Kunde noch f&uuml;r eine andere Ausgabeart als Preisschilder festlegen m&ouml;chte, ob die Ausstattung dort ausgeben werden soll und unter welcher Kategorie.<\/p>\n<p>Das Feld legen wir als Nachschlagefeld an, die ihre Werte aus der Tabelle <b>tblAusstattungskategorien <\/b>entnimmt. Warum als Nachschlagefeld? Weil so sp&auml;ter, wenn wir das Feld in ein Formular ziehen, direkt ein Kombinationsfeld auf Basis dieses Nachschlagefeldes erstellt wird. <\/p>\n<p>Au&szlig;erdem f&uuml;gen wir der Tabelle noch ein <b>Ja\/Nein<\/b>-Feld namens <b>Preisschild <\/b>hinzu. Die Tabelle sieht im Entwurf nun wie in Bild 5 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_006.png\" alt=\"Entwurf der Tabelle tblFahrzeugeAusstattungen mit den neuen Feldern\" width=\"649,559\" height=\"374,9765\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Entwurf der Tabelle tblFahrzeugeAusstattungen mit den neuen Feldern<\/span><\/b><\/p>\n<p>Wir stellen au&szlig;erdem noch die Eigenschaft <b>Beschriftung <\/b>der Felder wie folgt ein:<\/p>\n<ul>\n<li><b>AusstattungID<\/b>: <b>Ausstattung<\/b><\/li>\n<li><b>PreisschildAusstattungskategorieID<\/b>: <b>Kategorie<\/b><\/li>\n<li><b>AufPreisschild<\/b>: <b>Auf Preisschild<\/b><\/li>\n<\/ul>\n<p>Diese Beschriftungen werden sp&auml;ter bei der &Uuml;bernahme in das Formular &uuml;bernommen, sodass wir diese dort nicht nochmals anpassen m&uuml;ssen.<\/p>\n<p>Im Datenmodell spiegeln sich die Erweiterungen nun wie in Bild 6 wieder.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_005.png\" alt=\"Das Datenmodell mit der Tabelle f&uuml;r die Ausstattungskategorien\" width=\"649,559\" height=\"257,3045\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Das Datenmodell mit der Tabelle f&uuml;r die Ausstattungskategorien<\/span><\/b><\/p>\n<h2>Ausstattungen ausw&auml;hlbar machen<\/h2>\n<p>Schlie&szlig;lich wandeln wir das Feld <b>AusstattungID <\/b>noch in ein Nachschlagefeld um. Da wir daf&uuml;r bereits im oben genannten Beitrag eine Beziehung angelegt haben, f&uuml;gen wir die Nachschlagefeld-Eigenschaften kurzerhand manuell hinzu.<\/p>\n<p>Dazu aktivieren wir im Tabellenentwurf f&uuml;r das Feld <b>AusstattungID <\/b>in den Eigenschaften die Registerseite <b>Nachschlagen<\/b>.<\/p>\n<p>Hier stellen wir als Erstes den Wert der Eigenschaft <b>Steuerelement anzeigen <\/b>auf <b>Kombinationsfeld <\/b>ein. Dadurch blendet Access einige weitere Eigenschaften ein, die wir wie in Bild 7 anpassen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_007.png\" alt=\"Einstellen des Feldes AusstattungID als Nachschlagefeld\" width=\"649,559\" height=\"345,1891\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 7: Einstellen des Feldes AusstattungID als Nachschlagefeld<\/span><\/b><\/p>\n<p>Wichtig ist hier, dass die nachzuschlagenden Daten aus der Tabelle <b>tblAusstattungen <\/b>nach dem Namen der Ausstattungen sortiert ausgegeben werden.<\/p>\n<h2>Ideen f&uuml;r die Benutzeroberfl&auml;che<\/h2>\n<p>Wie aber wollen wir die Benutzeroberfl&auml;che gestalten? Wenn wir schon eine m:n-Verkn&uuml;pfungstabelle verwenden, wie sie auch beispielsweise bei Bestellungen, Bestellpositionen und Artikeln verwendet wird, k&ouml;nnen wir diese dann nicht auch so abbilden? Also so, dass wir in einem Hauptformular das Fahrzeug abbilden und im Unterformular in der Datenblattansicht die zugeordneten Ausstattungen? Mit den weiteren Steuerelementen, mit denen wir angeben, ob die Ausstattung auf dem Preisschild erscheinen soll und ob es sich um eine Serien- oder eine Sonderausstattung handelt?<\/p>\n<p>Das wird auf jeden Fall Bestandteil der L&ouml;sung sein, aber nicht allein. Wie beginnen jedoch erst einmal mit diesem Teil.<\/p>\n<h2>Fahrzeuge und Ausstattungen in Haupt- und Unterformular<\/h2>\n<p>Wir beginnen mit dem Unterformular namens <b>sfmFahrzeugeAusstattungenPreisschild<\/b>, dem wir die Tabelle <b>tblFahrzeugeAusstattungen <\/b>als <b>Datensatzquelle <\/b>zuweisen. Den Namen <b>sfmFahrzeugeAusstattungen <\/b>verwenden wir nicht, weil dieser in der Beispieldatenbank bereits f&uuml;r ein anderes Objekt vergeben ist.<\/p>\n<p>Wir ziehen die drei Felder <b>AusstattungID<\/b>, <b>PreisschildAusstattungskategorieID<\/b> und <b>AufPreisschild<\/b> in den Formularentwurf und stellen die Eigenschaft <b>Standardansicht <\/b>auf den Wert <b>Datenblatt <\/b>ein (siehe Bild 8). Danach speichern und schlie&szlig;en wir das Formular.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_008.png\" alt=\"Das Unterformular sfmFahrzeugeAusstattungenPreisschild\" width=\"599,559\" height=\"318,932\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 8: Das Unterformular sfmFahrzeugeAusstattungenPreisschild<\/span><\/b><\/p>\n<p>Das Hauptformular erstellen wir direkt im Anschluss. Es soll den Namen <b>frmFahrzeugeAusstattungenPreisschild <\/b>erhalten und verwendet die Tabelle <b>tblFahrzeuge <\/b>als <b>Datensatzquelle<\/b>. Wir ziehen lediglich die beiden Felder <b>FahrzeugID <\/b>und <b>Fahrzeug <\/b>in den Detailbereich des Formularentwurfs.<\/p>\n<p>Au&szlig;erdem ziehen wir dort das Formular <b>sfmFahrzeugeAusstattungenPreisschild <\/b>aus dem Navigationsbereich als Unterformular hinein.<\/p>\n<p>Wenn wir das Unterformular-Steuerelement markieren und im Eigenschaftenblatt die Seite <b>Daten <\/b>aktivieren, sollten die beiden Eigenschaften <b>Verkn&uuml;pfen von <\/b>und <b>Verkn&uuml;pfen nach <\/b>jeweils den Wert <b>FahrzeugID <\/b>enthalten (siehe Bild 9).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_009.png\" alt=\"Haupt- und Unterformular in der Entwurfsansicht\" width=\"599,559\" height=\"429,6304\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 9: Haupt- und Unterformular in der Entwurfsansicht<\/span><\/b><\/p>\n<p>Wechseln wir nun in die Formularansicht, sind wir eigentlich schon fertig: Das Hauptformular zeigt die Fahrzeuge an und im Unterformular k&ouml;nnen wir komfortabel die Ausstattungen ausw&auml;hlen. Die &uuml;brigen beiden Steuerelemente erlauben es uns, die Kategorie auszuw&auml;hlen und ob die Ausstattung auf dem Preisschild erscheinen soll (siehe Bild 10).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_010.png\" alt=\"Das Formular in Aktion\" width=\"499,5589\" height=\"370,1111\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 10: Das Formular in Aktion<\/span><\/b><\/p>\n<h2>Problem: Menge der Ausstattungsmerkmale<\/h2>\n<p>Allerdings haben wir ein Problem: Die Liste der Ausstattungsmerkmale enth&auml;lt mehr als 150 Eintr&auml;ge. Das hei&szlig;t also, dass das Zusammenstellen eines Fahrzeugs, das durchschnittlich vielleicht 30-50 Ausstattungen enth&auml;lt, ungef&auml;hr so viel Spa&szlig; macht wie die Steuererkl&auml;rung.<\/p>\n<p>Selbst wenn Mitarbeiter die Anfangsbuchstaben aller Ausstattungsmerkmale kennen und diese eingeben, dauert es doch noch eine Weile, bis man alle Daten eingegeben hat.<\/p>\n<h2>Einfachere Auswahl per Listenfeld<\/h2>\n<p>Also m&uuml;ssen wir hier noch nachlegen, um dem Benutzer ein schnelleres und effizienteres Arbeiten zu erm&ouml;glichen. Wir wollen dazu zun&auml;chst ein Listenfeld hinzuf&uuml;gen, das alle aktuell nicht ausgew&auml;hlten Ausstattungsmerkmale anzeigt und mit dem wir per Doppelklick weitere Elemente zur Auswahl hinzuf&uuml;gen k&ouml;nnen.<\/p>\n<p>Also verschieben wir das Unterformular in der Entwurfsansicht etwas nach rechts und f&uuml;gen links davon ein Listenfeld namens <b>lstAusstattungen <\/b>hinzu. Dieses soll einfach nur die Ausstattungen in alphabetischer Reihenfolge anzeigen und den Wert des Feldes <b>AusstattungID <\/b>der Tabelle <b>tblAusstattungen <\/b>als unsichtbare erste Spalte liefern.<\/p>\n<p>Die anzuzeigenden Ausstattungen h&auml;ngen jedoch, da wir nur die noch nicht zugeordneten Ausstattungsmerkmale ausgeben wollen, von den Eintr&auml;gen der Tabelle <b>tblFahrzeugeAusstattungen <\/b>ab, die mit dem aktuell im Hauptformular angezeigten Fahrzeug verkn&uuml;pft sind.<\/p>\n<p>Die Datensatzherkunft wollen wir daher per VBA zuweisen. Die dazu verwendete Prozedur hei&szlig;t <b>AusstattungslisteFuellen<\/b>. Wir rufen diese zu verschiedenen Gelegenheiten auf &#8211; zum Beispiel beim Anzeigen eines neuen Datensatzes im Hauptformular oder nach dem Hinzuf&uuml;gen eines der <b>Ausstattungsmerkmale <\/b>zur Tabelle <b>tblFahrzeugeAusstattungen<\/b>.<\/p>\n<p>Da die Abfrage, um die im Listenfeld anzuzeigenden Datens&auml;tze zu ermitteln, etwas komplizierter zu formulieren ist, nutzen wir den Abfrageeditor dazu. Wir wollen mit dieser Abfrage alle Datens&auml;tze der Tabelle <b>tblAusstattungen <\/b>liefern, die noch nicht mit einem Datensatz der Tabelle <b>tblFahrzeugeAusstattungen <\/b>verkn&uuml;pft sind.<\/p>\n<p>Dazu ben&ouml;tigen wir erst einmal eine Abfrage, die uns alle Ausstattungen liefert, die mit dem aktuellen Fahrzeug verkn&uuml;pft sind.<\/p>\n<p>Diese enth&auml;lt die Tabelle <b>tblAusstattungen <\/b>als Datenquelle. Wir ziehen beide Felder der Tabelle in das Entwurfsraster. F&uuml;r das Feld <b>AusstattungID <\/b>legen wir ein Kriterium fest, dass alle Datens&auml;tze liefert, deren Wert im Feld <b>AusstattungID <\/b>noch nicht &uuml;ber die Tabelle <b>tblFahrzeugeAusstattungen <\/b>mit einem Fahrzeug verkn&uuml;pft sind. Um welches Fahrzeug es sich handelt, geben wir zun&auml;chst &uuml;ber den Parameter <b>[prmFahrzeugID]<\/b> an (siehe Bild 11).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_011.png\" alt=\"Abfrage zur Ermittlung der noch nicht verwendeten Ausstattungen\" width=\"700\" height=\"291,5923\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 11: Abfrage zur Ermittlung der noch nicht verwendeten Ausstattungen<\/span><\/b><\/p>\n<p>Wechseln wir in die Datenblattansicht, erscheint eine Inputbox f&uuml;r die Eingabe der <b>FahrzeugID <\/b>des zu untersuchenden Fahrzeugs. Geben wir hier f&uuml;r unsere Beispieldaten den Wert <b>1 <\/b>ein, was dem Fahrzeug entspricht, dem wir die Ausstattungen mit den Bezeichnungen <b>Ausstattung 1 <\/b>und <b>Ausstattung 2 <\/b>zugeordnet haben, erhalten wir das Abfrageergebnis aus Bild 12.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_012.png\" alt=\"Die Abfrage qryLstAusstattungen\" width=\"424,5589\" height=\"163,5126\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 12: Die Abfrage qryLstAusstattungen<\/span><\/b><\/p>\n<p>Die Abfrage funktioniert also schon einmal. Nun wollen wir diese in die Funktion <b>AusstattungslisteFuellen <\/b>integrieren. Dazu wechseln wir in die SQL-Ansicht der Abfrage und kopieren den SQL-String in die Zwischenablage (siehe Bild 13).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_013.png\" alt=\"SQL-Ausdruck der Abfrage\" width=\"424,5589\" height=\"163,5126\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 13: SQL-Ausdruck der Abfrage<\/span><\/b><\/p>\n<p>Von hier f&uuml;gen wir die SQL-Anweisung in den VBA-Code der Prozedur <b>AusstattungslisteFuellen <\/b>ein, die wir im Klassenmodul des Formulars <b>frmFahrzeugeAusstattungenPreisschild <\/b>einf&uuml;gen.<\/p>\n<p>Die SQL-Anweisung erleichtern wir um einige unn&ouml;tige Elemente wie vorangestellte Tabellennamen und Klammern und erhalten dann eine Prozedur wie die folgende:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>AusstattungslisteFuellen()\r\n     <span style=\"color:blue;\">Dim <\/span>strSQL<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngFahrzeugID<span style=\"color:blue;\"> As Long<\/span>\r\n     lngFahrzeugID = Me!FahrzeugID\r\n     strSQL = \"SELECT AusstattungID, Ausstattung \" _\r\n         & \"FROM tblAusstattungen \" _\r\n         & \"WHERE AusstattungID \" _\r\n         & \"<span style=\"color:blue;\">Not<\/span> In (\" _\r\n         & \"  SELECT AusstattungID \" _\r\n         & \"  FROM tblFahrzeugeAusstattungen \" _\r\n         & \"  WHERE FahrzeugID = \" & lngFahrzeugID _\r\n         & \");\"\r\n     Me!lstAusstattungen.RowSource = strSQL\r\n<span style=\"color:blue;\">End Sub<\/span> <\/pre>\n<p>Hier ermitteln wir die ID des aktuell im Formular angezeigten Fahrzeugs und f&uuml;gen diese dann dort in den SQL-Ausdruck ein, wo sich zuvor der Parameter <b>[prmFahrzeugID] <\/b>befunden hat.<\/p>\n<p>Schlie&szlig;lich weist die Prozedur die so zusammengesetzte SQL-Anweisung der Eigenschaft <b>Datensatzherkunft <\/b>des Listenfeldes zu.<\/p>\n<p>Diese Prozedur rufen wir nun beispielsweise in der Ereignisprozedur auf, die durch das Ereignis <b>Form_Current <\/b>ausgel&ouml;st wird:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Current()\r\n     <span style=\"color:blue;\">Call<\/span> AusstattungslisteFuellen\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Danach brauchen wir nur noch f&uuml;r das Listenfeld <b>lstAusstattungen <\/b>die Eigenschaft <b>Spaltenanzahl <\/b>auf <b>2 <\/b>und <b>Spaltenbreiten <\/b>auf <b>0cm <\/b>einzustellen, damit nicht der Inhalt des Feldes <b>AusstattungID<\/b>, sondern der des Feldes <b>Ausstattung <\/b>angezeigt wird.<\/p>\n<p>Damit erhalten wir erst einmal die Ansicht des Formulars aus Bild 14.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_014.png\" alt=\"Das Listenfeld zeigt die verbleibenden Ausstattungen an.\" width=\"599,559\" height=\"367,7053\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 14: Das Listenfeld zeigt die verbleibenden Ausstattungen an.<\/span><\/b><\/p>\n<h2>Ausstattung per Doppelklick hinzuf&uuml;gen<\/h2>\n<p>Damit kommen wir zu dem Schritt, der dem Benutzer die Arbeit massiv erleichtern soll: Das Hinzuf&uuml;gen von Ausstattungen aus dem Listenfeld per Doppelklick. Dazu f&uuml;gen wir dem Listenfeld eine Ereignisprozedur hinzu, die durch das Ereignis <b>Beim Doppelklicken <\/b>ausgel&ouml;st wird (siehe Listing 1).<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>lstAusstattungen_DblClick(Cancel<span style=\"color:blue;\"> As Integer<\/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>strSQL<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngFahrzeugID<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngAusstattungID<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     lngFahrzeugID = Me!FahrzeugID\r\n     lngAusstattungID = Me!lstAusstattungen\r\n     strSQL = \"INSERT INTO tblFahrzeugeAusstattungen(FahrzeugID, AusstattungID) VALUES(\" & lngFahrzeugID & \", \" _\r\n         & lngAusstattungID & \")\"\r\n     db.Execute strSQL, dbFailOnError\r\n     Me!sfmFahrzeugeAusstattungenPreisschild.Form.Requery\r\n     <span style=\"color:blue;\">Call<\/span> AusstattungslisteFuellen\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Anpassung der Prozedur f&uuml;r das Vergr&ouml;&szlig;ern und Verkleinern<\/span><\/b><\/p>\n<p>Diese Prozedur ermittelt zun&auml;chst die Werte f&uuml;r die <b>FahrzeugID <\/b>und die <b>AusstattungID <\/b>f&uuml;r die Kombination aus Fahrzeug und Ausstattung, die zur Tabelle <b>tblFahrzeugeAusstattungen <\/b>hinzugef&uuml;gt werden soll. Dann stellt sie eine SQL-Abfrage wie die folgende zusammen und arbeitet dabei die <b>FahrzeugID <\/b>und die <b>AusstattungID <\/b>ein:<\/p>\n<pre>INSERT INTO tblFahrzeugeAusstattungen(FahrzeugID, AusstattungID) VALUES(1, 3)<\/pre>\n<p>Danach f&uuml;hrt sie diese Abfrage mit der <b>Execute<\/b>-Methode des <b>Database<\/b>-Objekts f&uuml;r die aktuelle Datenbank aus. Anschlie&szlig;end aktualisiert sie das Unterformular, sodass dieses nun die neu hinzugef&uuml;gte Ausstattung anzeigt. Au&szlig;erdem ruft es die Prozedur <b>AusstattungslisteFuellen <\/b>auf, damit das Listenfeld mit den neuen Daten aktualisiert wird.<\/p>\n<p>Das Ergebnis eines Doppelklicks auf einen Eintrag im Listenfeld sehen wir in Bild 15. Allerdings wurde noch keine <b>Kategorie <\/b>angelegt und auch das Feld <b>Auf Preisschild <\/b>wurde noch nicht aktualisiert. Hier haben wir nun folgende M&ouml;glichkeiten:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_015.png\" alt=\"Eine frisch hinzugef&uuml;gte Ausstattung\" width=\"549,559\" height=\"204,6512\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 15: Eine frisch hinzugef&uuml;gte Ausstattung<\/span><\/b><\/p>\n<ul>\n<li>Wir legen Standardwerte im Programmcode fest, die zum Beispiel daf&uuml;r sorgen, dass neue Ausstattungsmerkmale immer als Serienausstattung markiert werden und immer auf dem Preisschild des Fahrzeugs angezeigt werden sollen.<\/li>\n<li>Oder wir f&uuml;gen noch zwei Steuerelemente hinzu, mit denen wir festlegen, welche Eigenschaften neu hinzugef&uuml;gte Ausstattungen aufweisen sollen.<\/li>\n<\/ul>\n<p>Die erste Variante w&uuml;rde vielleicht bei 50% der hinzugef&uuml;gten Ausstattungen die richtigen Werte vorgeben.<\/p>\n<p>Bei der zweiten Variante k&ouml;nnte der Benutzer beispielsweise zuerst die Serienausstattungen hinzuf&uuml;gen und dann die Sonderausstattungen. Die Option <b>Auf Preisschild <\/b>k&ouml;nnte man einfach aktiviert lassen und jeweils &uuml;bernehmen und diese dann bei Bedarf nachtr&auml;glich f&uuml;r die betroffenen Ausstattungen einstellen.<\/p>\n<p>Wir zeigen, wie das mit der zweiten Variante funktioniert und f&uuml;gen dem Hauptformular zwei Steuerelemente hinzu:<\/p>\n<ul>\n<li><b>cboStandardkategorie<\/b>: Kombinationsfeld zur Auswahl der standardm&auml;&szlig;ig einzusetzenden Kategorie (Serienausstattung\/Sonderausstattung)<\/li>\n<li><b>chkAufPreisschild<\/b>: Kontrollk&auml;stchen, das angibt, ob neue Ausstattungen standardm&auml;&szlig;ig auf dem Preisschild angezeigt werden sollen<\/li>\n<\/ul>\n<p>Diese legen wir wie in Bild 16 an.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_016.png\" alt=\"Steuerelemente zum Festlegen der Standardwerte\" width=\"549,559\" height=\"196,2061\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 16: Steuerelemente zum Festlegen der Standardwerte<\/span><\/b><\/p>\n<p>F&uuml;r das Kombinationsfeld <b>cboStandardkategorie <\/b>hinterlegen wir die Tabelle <b>tblAusstattungskategorien <\/b>als <b>Datensatzherkunft<\/b>. Au&szlig;erdem stellen wir die beiden Eigenschaften <b>Spaltenanzahl <\/b>und <b>Spaltenbreiten <\/b>auf die Werte <b>2 <\/b>und <b>0cm <\/b>ein.<\/p>\n<h2>Tabelle zum Speichern von Optionen<\/h2>\n<p>Um diese Einstellungen zu speichern, ben&ouml;tigen wir allerdings noch eine Optionentabelle. Diese nennen wir <b>tblOptionen <\/b>und f&uuml;gen ihr die drei Felder <b>OptionID<\/b>, <b>StandardkategorieID <\/b>und <b>AufPreisschild <\/b>hinzu (siehe Bild 17).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_017.png\" alt=\"Die Tabelle tblOptionen\" width=\"549,559\" height=\"363,7804\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 17: Die Tabelle tblOptionen<\/span><\/b><\/p>\n<p>F&uuml;r das Feld <b>StandardkategorieID <\/b>legen wir ein Nachschlagefeld an, das die Daten der Tabelle <b>tblAusstattungskategorien <\/b>enth&auml;lt. <b>AufPreisschild <\/b>ist ein einfaches <b>Ja\/Nein<\/b>-Feld.<\/p>\n<h2>Steuerelemente mit Optionen f&uuml;llen und synchronisieren<\/h2>\n<p>Wir k&ouml;nnen die Steuerelemente <b>cboStandardkategorie <\/b>und <b>chkAufPreisschild <\/b>in diesem Formular leider nicht an die Felder dieser Tabelle binden, da das Formular bereits an die Tabelle <b>tblFormulare <\/b>gebunden ist.<\/p>\n<p>Daher haben wir nun verschiedene M&ouml;glichkeiten:<\/p>\n<ul>\n<li>Wir f&uuml;llen die Steuerelemente per VBA mit den Werten aus der Tabelle <b>tblOptionen <\/b>und schrieben diese zur&uuml;ck, wenn der Benutzer die Werte &auml;ndert.<\/li>\n<li>Wir erstellen ein Unterformular, das wir an die Tabelle <b>tblOptionen <\/b>binden und das die entsprechenden Steuerelemente enth&auml;lt. Dieses f&uuml;gen wir anstelle der bisher verwendeten Steuerelemente in das Formular <b>frmFahrzeugeAusstattungenPreisschild <\/b>ein.<\/li>\n<li>Oder wir erstellen ein komplett eigenes Formular, das wir &uuml;ber das Ribbon oder vom Formular <b>frmFahrzeugeAusstattuntenPreisschild <\/b>aus &ouml;ffnen.<\/li>\n<\/ul>\n<p>Da wir die Steuerelemente nun bereits angelegt haben, w&auml;hlen wir Option Nummer 2.<\/p>\n<p>Daher f&uuml;gen wir Code zum Formularmodul hinzu, der beim Laden des Formulars ausgef&uuml;hrt wird und die Daten f&uuml;r diese Optionen in die Steuerelemente schreibt. Beim &Auml;ndern der Option wird diese in die Tabelle zur&uuml;ckgeschrieben.<\/p>\n<p>Wir starten mit der Prozedur, die beim Ausw&auml;hlen eines Eintrags im Kombinationsfeld <b>cboStandardkategorie <\/b>ausgel&ouml;st wird. Diese finden wir in Listing 2. Die Prozedur f&uuml;hrt eine <b>UPDATE<\/b>-Abfrage aus, die das Feld <b>StandardkategorieID <\/b>in der Tabelle <b>tblOptionen <\/b>auf den aktuellen Wert des Steuerelements <b>cboStandardkategorie <\/b>einstellt.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cboStandardkategorie_AfterUpdate()\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 \"UPDATE tblOptionen SET StandardkategorieID = \" & Me!cboStandardkategorie, dbFailOnError\r\n     <span style=\"color:blue;\">If <\/span>db.RecordsAffected = 0<span style=\"color:blue;\"> Then<\/span>\r\n         db.Execute \"INSERT INTO tblOptionen(StandardkategorieID) VALUES(\" & Me!cboStandardkategorie _\r\n             & \")\", dbFailOnError\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Aktualisieren des Standards f&uuml;r die Ausstattungskategorie<\/span><\/b><\/p>\n<p>Danach pr&uuml;ft die Prozedur, wie viele Datens&auml;tze von dieser &Auml;nderungsabfrage betroffen waren. Wenn nicht kein Datensatz betroffen ist, hat die Prozedur ihre Aufgabe bereits erledigt.<\/p>\n<p>Sollte jedoch kein Datensatz von der &Auml;nderung betroffen sein, ist vermutlich noch kein Datensatz in der Tabelle <b>tblOptionen <\/b>enthalten und die folgende <b>INSERT INTO<\/b>-Anweisung f&uuml;gt einen neuen Datensatz hinzu, wobei das Feld <b>StandardkategorieID <\/b>automatisch auf den aktuellen Wert des Kombinationsfeldes eingestellt wird.<\/p>\n<p>Die Prozedur, die durch das &Auml;ndern des Wertes im Kontrollk&auml;stchen <b>chkAufPreisschild <\/b>ausgel&ouml;st wird, funktioniert auf die gleiche Weise (siehe Listing 3). Auch sie versucht zun&auml;chst, einen vorhandenen Datensatz zu bearbeiten. Ist das nicht erfolgreich, wird auch hier ein neuer Datensatz angelegt, der direkt mit dem Wert des Kontrollk&auml;stchens im Feld <b>AufPreisschild <\/b>ausgestattet wird.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>chkAufPreisschild_AfterUpdate()\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 \"UPDATE tblOptionen SET AufPreisschild = \" & Me!chkAufPreisschild, dbFailOnError\r\n     <span style=\"color:blue;\">If <\/span>db.RecordsAffected = 0<span style=\"color:blue;\"> Then<\/span>\r\n         db.Execute \"INSERT INTO tblOptionen(AufPreisschild) VALUES(\" & Me!chkAufPreisschild & \")\", dbFailOnError\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 3: Aktualisieren des Standards f&uuml;r die Ausgabe auf dem Preisschild<\/span><\/b><\/p>\n<p>Wenn wir auf diese Weise eine Standardkategorie ausgew&auml;hlt haben und festgelegt haben, dass die neu hinzugef&uuml;gten Ausstattungen standardm&auml;&szlig;ig auf dem Preisschild landen sollen, sieht die Tabelle wie in Bild 18 aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_018.png\" alt=\"Die Tabelle tblOptionen mit frisch ge&auml;nderten Werten\" width=\"549,559\" height=\"179,6635\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 18: Die Tabelle tblOptionen mit frisch ge&auml;nderten Werten<\/span><\/b><\/p>\n<p>Damit kommen wir zum Anzeigen dieser Werte, wenn der Benutzer das Formular neu &ouml;ffnet.<\/p>\n<p>Dazu f&uuml;gen wir eine Ereignisprozedur f&uuml;r das Ereignis <b>Beim Laden <\/b>hinzu.<\/p>\n<p>Diese f&uuml;llen wir mit den Anweisungen aus Listing 4. Wir stellen sowohl das Kombinationsfeld <b>cboStandardkategorie <\/b>als auch das Kontrollk&auml;stchen <b>chkAufPreisschild <\/b>mit dem Werten aus der Tabelle <b>tblOptionen<\/b>, die wir mit entsprechenden <b>DLookup<\/b>-Funktionen auslesen.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     Me!cboStandardkategorie = Nz(DLookup(\"StandardkategorieID\", \"tblOptionen\"), 0)\r\n     Me!chkAufPreisschild = Nz(DLookup(\"AufPreisschild\", \"tblOptionen\"), 0)\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Einlesen der Optionen in die Steuerelemente des Formulars<\/span><\/b><\/p>\n<h2>Standardwerte beim Hinzuf&uuml;gen von Ausstattungsmerkmalen ber&uuml;cksichtigen<\/h2>\n<p>Damit diese Einstellungen sich beim Hinzuf&uuml;gen von Ausstattungen zu einem Fahrzeug bemerkbar machen, passen wir die Prozedur <b>lstAusstattungen_DblClick <\/b>noch ein wenig an. Genauer gesagt f&uuml;gen wir nur zwei Felder zu der <b>INSERT INTO<\/b>-Anweisung hinzu, welche die Werte der beiden neuen Kontrollk&auml;stchen direkt in die neuen Zuweisungen von Ausstattungen zum Fahrzeug schreiben (siehe Listing 5).<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>lstAusstattungen_DblClick(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n     ...\r\n     strSQL = \"INSERT INTO tblFahrzeugeAusstattungen(FahrzeugID, AusstattungID, PreisschildAusstattungskategorieID, \" _\r\n         & \"AufPreisschild) VALUES(\" & lngFahrzeugID & \", \" & lngAusstattungID & \", \" & Me!cboStandardkategorie _\r\n         & \", \" & Me!chkAufPreisschild & \")\"\r\n     db.Execute strSQL, dbFailOnError\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 5: Anpassungen der Prozedur zum Hinzuf&uuml;gen von Ausstattungen<\/span><\/b><\/p>\n<p>Probieren wir dies nun aus, nachdem wir die Standardwerte festgelegt haben, erscheinen nun auch die gew&uuml;nschten Eigenschaften im Unterformular <b>sfmFahrzeugeAusstattungenPreisschild<\/b> (siehe Bild 19).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_019.png\" alt=\"Die Ausstattungen werden nun mit den Standardwerten angelegt.\" width=\"549,559\" height=\"325,4828\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 19: Die Ausstattungen werden nun mit den Standardwerten angelegt.<\/span><\/b><\/p>\n<h2>Jede Ausstattung nur einmal pro Fahrzeug<\/h2>\n<p>Wir k&ouml;nnen wir die Einstellungen nachtr&auml;glich &auml;ndern. Au&szlig;erdem k&ouml;nnen wir auch manuell Eintr&auml;ge zu den Ausstattungen hinzuf&uuml;gen, indem wir einen Wert in der Spalte <b>Ausstattung <\/b>ausw&auml;hlen.<\/p>\n<p>Allerdings k&ouml;nnen wir auf diese Weise aktuell auch noch eine Ausstattung ein zweites Mal zu einem Fahrzeug hinzuf&uuml;gen.<\/p>\n<p>Das wollen wir noch &auml;ndern. Dazu stellen wir f&uuml;r die beiden Felder <b>FahrzeugID <\/b>und <b>AusstattungID <\/b>in der Tabelle <b>tblFahrzeugeAusstattungen <\/b>einen eindeutigen, zusammengesetzten Prim&auml;rschl&uuml;ssel ein (siehe Bild 20).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_020.png\" alt=\"Hinzuf&uuml;gen eines eindeutigen Indexes f&uuml;r die Felder FahrzeugID und AusstattungID\" width=\"649,559\" height=\"387,0551\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 20: Hinzuf&uuml;gen eines eindeutigen Indexes f&uuml;r die Felder FahrzeugID und AusstattungID<\/span><\/b><\/p>\n<p>Diesen legen wir &uuml;ber den Dialog Indizes: <b>tblFahrzeugeAusstattungen <\/b>fest, den wir mit einem Rechtsklick auf die Titelleiste der Tabelle und anschlie&szlig;ender Auswahl des Eintrags <b>Indizes <\/b>erhalten.<\/p>\n<p>Hier geben wir den Namen <b>UniqueKey <\/b>ein und w&auml;hlen rechts daneben und in der Zeile darunter die Felder <b>FahrzeugID <\/b>und <b>AusstattungID <\/b>aus. Au&szlig;erdem legen wir f&uuml;r den Index die Eigenschaft <b>Eindeutig <\/b>auf <b>Ja <\/b>fest.<\/p>\n<p>Wenn wir nun beispielsweise nochmals die Ausstattung namens <b>Ausstattung 4 <\/b>zu einem Fahrzeug hinzuf&uuml;gen, erhalten wir eine Meldung wie die aus Bild 21.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_021.png\" alt=\"Fehler, der beim Hinzuf&uuml;gen einer bereits vorhandenen Ausstattung ausgel&ouml;st wird.\" width=\"649,559\" height=\"429,7082\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 21: Fehler, der beim Hinzuf&uuml;gen einer bereits vorhandenen Ausstattung ausgel&ouml;st wird.<\/span><\/b><\/p>\n<p>Damit diese f&uuml;r den Benutzer normalerweise recht unverst&auml;ndliche Meldung nicht erscheint, ersetzen wir diese durch eine eigene Meldung. Dazu f&uuml;gen wir der Ereigniseigenschaft <b>Bei Fehler <\/b>des Unterformulars die Ereignisprozedur aus Listing 6 hinzu. Diese erh&auml;lt mit dem Parameter <b>DataErr <\/b>die Fehlernummer.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Error(DataErr<span style=\"color:blue;\"> As Integer<\/span>, Response<span style=\"color:blue;\"> As Integer<\/span>)\r\n     Select Case DataErr\r\n         <span style=\"color:blue;\">Case <\/span>3022\r\n             <span style=\"color:blue;\">MsgBox<\/span> \"Sie d&uuml;rfen jede Ausstattung nur einmal zu einem Fahrzeug hinzuf&uuml;gen.\", vbOKOnly + _\r\n                 vbExclamation, \"Doppelte Zuweisung\"\r\n             Response = acDataErrContinue\r\n     <span style=\"color:blue;\">End Select<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 6: Fehlerbehandlung f&uuml;r Formularfehler<\/span><\/b><\/p>\n<p>Lautet dieser auf <b>3022<\/b>, haben wir es mit dem Fehler zu tun, den wir zuvor angezeigt bekommen haben. In diesem Fall geben wir mit einer <b>MsgBox<\/b>-Funktion unsere eigene, verst&auml;ndlichere Fehlermeldung aus. Danach stellen wir Response auf <b>acDataErrContinue <\/b>ein, damit die eigentliche Fehlermeldung unterbunden wird.<\/p>\n<p>Diese erscheint schlie&szlig;lich wie in Bild 22.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_022.png\" alt=\"Neue, benutzerdefinierte Fehlermeldung beim Auftreten eines Fehlers\" width=\"424,5589\" height=\"161,3324\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 22: Neue, benutzerdefinierte Fehlermeldung beim Auftreten eines Fehlers<\/span><\/b><\/p>\n<h2>Entfernen von Ausstattungen aus dem Datenblatt<\/h2>\n<p>Damit haben wir nun zwar eine komfortable M&ouml;glichkeit zum Hinzuf&uuml;gen von Ausstattungen zu einem Fahrzeug geschaffen. Was aber, wenn wir eine Ausstattung versehentlich zu einem Fahrzeug hinzuf&uuml;gt haben? Dann k&ouml;nnen wir diese nat&uuml;rlich auf herk&ouml;mmlichem Wege wieder l&ouml;schen. Dazu klicken wir den Datensatzmarkierer und bet&auml;tigen die <b>L&ouml;schen<\/b>-Schaltfl&auml;che.<\/p>\n<p>Das funktioniert grunds&auml;tzlich, jedoch wird das entfernte Ausstattungsmerkmal nicht direkt wieder im Listenfeld angezeigt.<\/p>\n<p>Wir m&uuml;ssen also daf&uuml;r sorgen, dass das Listenfeld im Hauptformular aktualisiert wird, wenn der Benutzer einen Datensatz im Unterformular l&ouml;scht.<\/p>\n<p>Es w&auml;re praktisch, wenn wir dazu die Prozedur <b>AusstattungslisteFuellen<\/b> des Hauptformulars vom Unterformular aus aufrufen k&ouml;nnten. Dazu m&uuml;ssen wir diese &ouml;ffentlich machen, was wir durch &Auml;ndern des Schl&uuml;sselworts <b>Private <\/b>in <b>Public <\/b>erreichen:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>AusstattungslisteFuellen()\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Dann f&uuml;gen wir im Unterformular f&uuml;r das Ereignis <b>Beim L&ouml;schen <\/b>die folgende Ereignisprozedur hinzu:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Delete(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n     <span style=\"color:blue;\">Call<\/span> Me.Parent.AusstattungslisteFuellen\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Das funktioniert allerdings nicht wie gew&uuml;nscht. Die Ereignisprozedur wird zwar, wie wir durch Setzen eines Haltepunktes ermittelt haben, beim L&ouml;schen eines Datensatzes im Unterformular ausgel&ouml;st. Wenn wir diesen Schritt f&uuml;r Schritt durchlaufen, landen wir auch in der Prozedur <b>AusstattungslisteFuellen<\/b>. Allerdings erhalten wir dort am Ende der Prozedur manchmal den Fehler aus Bild 23.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_023.png\" alt=\"Fehler beim Versuch, beim L&ouml;schen direkt das Listenfeld zu aktualisieren\" width=\"424,5589\" height=\"154,5072\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 23: Fehler beim Versuch, beim L&ouml;schen direkt das Listenfeld zu aktualisieren<\/span><\/b><\/p>\n<h2>Alternative: Unterformularereignisse vom Hauptformular aus implementieren<\/h2>\n<p>Wir haben dann als Erstes probiert, die Funktionalit&auml;t vom Unterformular in das Hauptformular zu ziehen. Dies ist ohnehin eine praktische Vorgehensweise, bei der man alle Funktionen aus einem Klassenmodul heraus steuert. Dazu haben wir die folgenden Schritte durchgef&uuml;hrt.<\/p>\n<p>Als Erstes haben wir im Klassenmodul des Hauptformulars eine Objektvariable angelegt, mit der wir das Unterformular referenzieren k&ouml;nnen. Dabei verwenden wir das Schl&uuml;sselwort <b>WithEvents<\/b>, damit wir auch die Ereignisse dieses Objekts im aktuellen Klassenmodul implementieren k&ouml;nnen:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>WithEvents sfm<span style=\"color:blue;\"> As <\/span>Form<\/pre>\n<p>Dann haben wir im Ereignis <b>Form_Load <\/b>des Hauptformulars zwei Zeilen hinzugef&uuml;gt, von denen die erste der Variablen <b>sfm <\/b>einen Verweis auf das Unterformular zuweist und die zweite die Eigenschaft <b>OnDelete <\/b>des Unterformulars auf den Wert <b>[Event Procudure] <\/b>einstellt.:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     <span style=\"color:blue;\">Set<\/span> sfm = Me.sfmFahrzeugeAusstattungenPreisschild.Form\r\n     sfm.OnDelete = \"[Event Procedure]\"\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Dies ist gleichbedeutend damit, im Eigenschaftenblatt des Unterformulars f&uuml;r die Eigenschaft <b>Beim L&ouml;schen <\/b>den Wert <b>[Ereignisprozedur] <\/b>einzutragen. Nun m&uuml;ssen wir noch das Ereignis implementieren. Dazu w&auml;hlen wir im linken Kombinationsfeld des Codefensters des Klassenmoduls des Hauptformulars den Eintrag <b>sfm <\/b>aus. Im rechten Kombinationsfeld selektieren wir den Eintrag <b>Delete<\/b>. In die so erzeugte Ereignisprozedur <b>sfm_Delete <\/b>tragen wir nun die Zeile ein, mit der wir die Prozedur <b>AusstattungslisteFuellen <\/b>aufrufen:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>sfm_Delete(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n     <span style=\"color:blue;\">Call<\/span> AusstattungslisteFuellen\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Nach dem erneuten Anzeigen des Formulars in der Formularansicht erhalten wir allerdings immer noch die Fehlermeldung von oben. <\/p>\n<h2>Per Timer auf L&ouml;schen reagieren<\/h2>\n<p>Die n&auml;chste Idee in solchen F&auml;llen ist immer, dass es sich um ein Timing-Problem handeln k&ouml;nnte. Die Fehlermeldung besagte, dass die Operation in Transaktionen nicht unterst&uuml;tzt wird. Gegebenenfalls m&uuml;ssen wir also nur eine Zehntelsekunde warten. Dazu f&uuml;hren wir zwei Schritte aus. Der erste ist, dass wir in der Ereignisprozedur, die beim L&ouml;schen eines Eintrags im Unterformular ausgel&ouml;st wird, den Aufruf von <b>AusstattungslisteFuellen <\/b>entfernen und stattdessen einen Timer starten. Das erledigen wir durch Einstellen der Eigenschaft <b>TimerInterval <\/b>auf einen Wert wie <b>100 <\/b>(Millisekunden):<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>sfm_Delete(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n     Me.TimerInterval = 100\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Dadurch wird in 100 Millisekunden die Ereignisprozedur aufgerufen, die wir f&uuml;r das Ereignis <b>Bei Zeitgeber <\/b>des Hauptformulars hinterlegen. Diese f&uuml;hrt nun zwei Anweisungen aus. Zuerst den urspr&uuml;nglichen Aufruf der Prozedur <b>AusstattungslisteFuellen <\/b>und dann wird die Eigenschaft <b>TimerInterval <\/b>wieder auf den Wert <b>0<\/b> eingestellt, damit der Timer anschlie&szlig;end nicht weiterl&auml;uft:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Timer()\r\n     <span style=\"color:blue;\">Call<\/span> AusstattungslisteFuellen\r\n     Me.TimerInterval = 0\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Und siehe da &#8211; danach werden die Ausstattungen, die wir aus dem Unterformular l&ouml;schen, direkt wieder im Listenfeld bereitgestellt.<\/p>\n<h2>Fehler bei Doppelklick in den leeren Bereich im Listenfeld<\/h2>\n<p>Nun haben wir aus Versehen beim schnellen Hinzuf&uuml;gen einiger Ausstattungen auf den leeren Bereich unter dem letzten Eintrag im Listenfeld geklickt. Das hat den Fehler aus Bild 24 hervorgerufen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_024.png\" alt=\"Fehler beim Doppelklick auf den leeren Bereich im Listenfeld\" width=\"424,5589\" height=\"240,9913\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 24: Fehler beim Doppelklick auf den leeren Bereich im Listenfeld<\/span><\/b><\/p>\n<p>Was ist hier passiert? Der Doppelklick l&ouml;st die Prozedur <b>lstAusstattungen_Doppelklick <\/b>auf. Diese stellt eine <b>INSERT INTO<\/b>-Abfrage zusammen, welche unter anderem f&uuml;r das Feld <b>AusstattungID <\/b>den aktuellen Wert des Listenfeldes eintragen soll.<\/p>\n<p>Diesen ermitteln wir mit <b>Me!lstAusstattungen<\/b>, schreiben diesen Wert in die Variable <b>lngAusstattungID<\/b> und f&uuml;gen ihren Inhalt als Wert in die SQL-Abfrage ein. Man sollte meinen, dass das Listenfeld nun den Wert <b>Null<\/b> aufweist und gegebenenfalls der Fehler aus Bild 25 erscheint. Das geschieht tats&auml;chlich, wenn wir direkt nach dem &Ouml;ffnen des Formulars auf den leeren Bereich im Listenfeld klicken &#8211; das Listenfeld hat den Wert <b>Null<\/b>, was beim Zuweisen zu der Variablen <b>lngAusstattungID <\/b>zum Fehler f&uuml;hrt.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_04\/pic_1510_025.png\" alt=\"Fehler, wenn man direkt nach dem &Ouml;ffnen in den leeren Bereich des Listenfeldes klickt\" width=\"424,5589\" height=\"240,9913\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 25: Fehler, wenn man direkt nach dem &Ouml;ffnen in den leeren Bereich des Listenfeldes klickt<\/span><\/b><\/p>\n<p>Aber der von uns beobachtete Fehler ist ein ganz anderer. Hier wird offensichtlich versucht, einen Datensatz zur Tabelle <b>tblFahrzeugeAusstattungen <\/b>hinzuzuf&uuml;gen, dessen Kombination aus <b>FahrzeugID <\/b>und <b>AusstattungID <\/b>bereits vorhanden ist. Das w&uuml;rde aber bedeuten, dass ein Wert aus dem Listenfeld in der Variablen <b>lngAusstattungID <\/b>landet, der bereits vorhanden ist. Aber wie kann das sein, wenn wir den entsprechenden Eintrag zuvor aus der Datensatzherkunft des Formulars entfernt haben?<\/p>\n<p>Offenbar handelt es sich hierbei um einen Bug. Das Listenfeld soll normalerweise den Wert zur&uuml;ckgeben, der dem Wert in der gebundenen Spalte f&uuml;r den aktuell markierten Datensatz entspricht. Allerdings ist der Datensatz mit dem Wert, den das Listenfeld zur&uuml;ckgibt, gar nicht mehr vorhanden. Nach einigen Versuchen sieht es so aus, als ob das Listenfeld auch nach dem Zuweisen einer neuen Datensatzquelle und sogar nach dem Requery nicht den Wert l&ouml;scht, den es zuvor durch das Markieren eines Eintrags erhalten hat. Das scheint ein Bug zu sein. Wir umgehen dies, indem wir nach dem L&ouml;schen per Doppelklick den Wert des Listenfeldes wieder auf <b>Null<\/b> einstellen:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>AusstattungslisteFuellen()\r\n     ...\r\n     Me!lstAusstattungen.RowSource = strSQL\r\n     Me!lstAusstattungen.Value = Null\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Dadurch erhalten wir nun beim Anklicken des leeren Bereichs im Listenfeld immer den Fehler aus dem letzten Screenshot, weil der Wert des Listenfeldes <b>Null <\/b>ist und wir versuchen, diesen der Variablen <b>lngAusstattungID <\/b>zuzuweisen. Das verhindern wir in der Prozedur <b>lstAusstattungen_DblClick<\/b>, indem wir pr&uuml;fen, ob <b>lstAusstattungen <\/b>den Wert <b>Null <\/b>aufweist. In diesem Fall geben wir eine entsprechende Meldung aus und verlassen die Prozedur:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>lstAusstattungen_DblClick(Cancel<span style=\"color:blue;\"> As Integer<\/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>strSQL<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngFahrzeugID<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngAusstattungID<span style=\"color:blue;\"> As Long<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     lngFahrzeugID = Me!FahrzeugID\r\n     <span style=\"color:blue;\">If <\/span>IsNull(Me!lstAusstattungen)<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Bitte klicke doppelt auf einen der \" _\r\n             & \"Eintr&auml;ge, um diesen zum Fahrzeug hinzuzuf&uuml;gen.\"\r\n         <span style=\"color:blue;\">Exit Sub<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     ...\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Damit k&ouml;nnen wir nun gefahrlos in allen Bereichen des Listenfeldes Doppelklicks ausf&uuml;hren. Wenn wir dabei auf einen der vorhandenen Eintr&auml;ge klicken, wird dieser in die Liste der Ausstattungen f&uuml;r das aktuell im Hauptformular angezeigte Fahrzeug eingetragen.<\/p>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Dieser Beitrag zeigt an einem Praxisbeispiel, wie man die Verwaltung von m:n-Beziehungen mit zwei Listenfeldern und mit einem Unterformular kombinieren kann und so das Beste aus beiden Welten erh&auml;lt. Mit den hier verwendeten Beispieldaten l&auml;sst sich allerdings der eigentliche Komfort noch gar nicht absehen. Wenn man f&uuml;r Ausstattungen von Fahrzeugen alle relevanten Eintr&auml;ge ber&uuml;cksichtigt, wird die Liste schnell &uuml;ber 150 Eintr&auml;ge lang. Dann ben&ouml;tigen wir allerdings vielleicht noch die eine oder andere Optimierung. Praktisch w&auml;re es dann beispielsweise, wenn der Benutzer nicht durch das gesamte Listenfeld scrollen m&uuml;sste, um beispielsweise die unteren Eintr&auml;ge auszuw&auml;hlen. Stattdessen w&auml;re eine Filterfunktion hilfreich, mit welcher der Benutzer schnell zu den gesuchten Ausstattungen gelangen w&uuml;rde. Wie dies gelingt, zeigen wir in einem weiteren Beitrag namens <b>Schnellsuche im Listenfeld mal anders <\/b>(<b>www.access-im-unternehmen.de\/1509<\/b>).<\/p>\n<p>Wenn wir noch etwas weiterdenken, wird der Benutzer die ausgew&auml;hlten Ausstattungen in einem Bericht ausgeben wollen, um die Preisschilder f&uuml;r seine Fahrzeuge zu generieren. Dann stellt sich die Frage: Passen alle Ausstattungsmerkmale auf die Seite? Moderne Autos haben mehr Funktionen, die f&uuml;r den K&auml;ufer zu einer Kaufentscheidung beitragen k&ouml;nnen, als man denkt. Wenn jedoch nicht alle Ausstattungen auf dem Preisschild abgebildet werden k&ouml;nnen, wollen wir dem Benutzer eine einfache M&ouml;glichkeit bieten, dies fr&uuml;hzeitig zu erkennen. Es w&auml;re also noch eine spannende Aufgabe, zu ermitteln, wieviele Ausstattungsmerkmale auf das Preisschild passen &#8230;<\/p>\n<h2>Downloads zu diesem Beitrag<\/h2>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>MnDatenWieImMehrwertigenFeldSelektieren.accdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/4B8B74E3-E4DF-4BCB-8934-A7D24EADCD49\/aiu_1510.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Es gibt verschiedene M&ouml;glichkeiten, eine m:n-Beziehung zwischen zwei Tabellen in Formularen abzubilden: Mit zwei Listenfeldern, mit einem Haupt- und einem Unterformular und viele weitere. In diesem Beitrag schauen wir uns eine Kombination aus Listenfeld und Datenblatt an. Dabei betrachten wir das Beispiel von Fahrzeugen und Ausstattungsmerkmalen. Eigentlich sollte man meinen, das w&auml;re eine reine m:n-Beziehung, in der die Verkn&uuml;pfungstabelle nur die Zuordnung der Merkmale zu den Fahrzeugen vornimmt. Allerdings liefert einer unserer Kunden ein Beispiel, bei dem es etwas aufwendiger wird: Zus&auml;tzlich zur reinen Zuordnung soll auch noch festgelegt werden k&ouml;nnen, welche Ausstattungsmerkmale mit auf das Preisschild sollen und welche Serien- und welche Sonderausstattungen es gibt. Kein Problem: Dann bauen wir einfach eine ergonomische L&ouml;sung f&uuml;r diesen Fall, wie dieser Beitrag zeigt.<\/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":[662024,66042024,44000023],"tags":[],"class_list":["post-55001510","post","type-post","status-publish","format-standard","hentry","category-662024","category-66042024","category-Mit_Formularen_arbeiten"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.9 (Yoast SEO v27.4) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>m:n-Beziehung mit Listenfeld und Datenblatt - 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\/mnBeziehung_mit_Listenfeld_und_Datenblatt\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"m:n-Beziehung mit Listenfeld und Datenblatt\" \/>\n<meta property=\"og:description\" content=\"Es gibt verschiedene M&ouml;glichkeiten, eine m:n-Beziehung zwischen zwei Tabellen in Formularen abzubilden: Mit zwei Listenfeldern, mit einem Haupt- und einem Unterformular und viele weitere. In diesem Beitrag schauen wir uns eine Kombination aus Listenfeld und Datenblatt an. Dabei betrachten wir das Beispiel von Fahrzeugen und Ausstattungsmerkmalen. Eigentlich sollte man meinen, das w&auml;re eine reine m:n-Beziehung, in der die Verkn&uuml;pfungstabelle nur die Zuordnung der Merkmale zu den Fahrzeugen vornimmt. Allerdings liefert einer unserer Kunden ein Beispiel, bei dem es etwas aufwendiger wird: Zus&auml;tzlich zur reinen Zuordnung soll auch noch festgelegt werden k&ouml;nnen, welche Ausstattungsmerkmale mit auf das Preisschild sollen und welche Serien- und welche Sonderausstattungen es gibt. Kein Problem: Dann bauen wir einfach eine ergonomische L&ouml;sung f&uuml;r diesen Fall, wie dieser Beitrag zeigt.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/mnBeziehung_mit_Listenfeld_und_Datenblatt\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2024-08-01T09:54:27+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg02.met.vgwort.de\/na\/099a42723ed64bb19d9b294e6a753455\" \/>\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=\"28\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/mnBeziehung_mit_Listenfeld_und_Datenblatt\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/mnBeziehung_mit_Listenfeld_und_Datenblatt\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"m:n-Beziehung mit Listenfeld und Datenblatt\",\"datePublished\":\"2024-08-01T09:54:27+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/mnBeziehung_mit_Listenfeld_und_Datenblatt\\\/\"},\"wordCount\":5131,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/mnBeziehung_mit_Listenfeld_und_Datenblatt\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/099a42723ed64bb19d9b294e6a753455\",\"articleSection\":[\"2024\",\"4\\\/2024\",\"Mit Formularen arbeiten\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/mnBeziehung_mit_Listenfeld_und_Datenblatt\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/mnBeziehung_mit_Listenfeld_und_Datenblatt\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/mnBeziehung_mit_Listenfeld_und_Datenblatt\\\/\",\"name\":\"m:n-Beziehung mit Listenfeld und Datenblatt - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/mnBeziehung_mit_Listenfeld_und_Datenblatt\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/mnBeziehung_mit_Listenfeld_und_Datenblatt\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/099a42723ed64bb19d9b294e6a753455\",\"datePublished\":\"2024-08-01T09:54:27+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/mnBeziehung_mit_Listenfeld_und_Datenblatt\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/mnBeziehung_mit_Listenfeld_und_Datenblatt\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/mnBeziehung_mit_Listenfeld_und_Datenblatt\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/099a42723ed64bb19d9b294e6a753455\",\"contentUrl\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/099a42723ed64bb19d9b294e6a753455\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/mnBeziehung_mit_Listenfeld_und_Datenblatt\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"m:n-Beziehung mit Listenfeld und Datenblatt\"}]},{\"@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":"m:n-Beziehung mit Listenfeld und Datenblatt - 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\/mnBeziehung_mit_Listenfeld_und_Datenblatt\/","og_locale":"de_DE","og_type":"article","og_title":"m:n-Beziehung mit Listenfeld und Datenblatt","og_description":"Es gibt verschiedene M&ouml;glichkeiten, eine m:n-Beziehung zwischen zwei Tabellen in Formularen abzubilden: Mit zwei Listenfeldern, mit einem Haupt- und einem Unterformular und viele weitere. In diesem Beitrag schauen wir uns eine Kombination aus Listenfeld und Datenblatt an. Dabei betrachten wir das Beispiel von Fahrzeugen und Ausstattungsmerkmalen. Eigentlich sollte man meinen, das w&auml;re eine reine m:n-Beziehung, in der die Verkn&uuml;pfungstabelle nur die Zuordnung der Merkmale zu den Fahrzeugen vornimmt. Allerdings liefert einer unserer Kunden ein Beispiel, bei dem es etwas aufwendiger wird: Zus&auml;tzlich zur reinen Zuordnung soll auch noch festgelegt werden k&ouml;nnen, welche Ausstattungsmerkmale mit auf das Preisschild sollen und welche Serien- und welche Sonderausstattungen es gibt. Kein Problem: Dann bauen wir einfach eine ergonomische L&ouml;sung f&uuml;r diesen Fall, wie dieser Beitrag zeigt.","og_url":"https:\/\/access-im-unternehmen.de\/mnBeziehung_mit_Listenfeld_und_Datenblatt\/","og_site_name":"Access im Unternehmen","article_published_time":"2024-08-01T09:54:27+00:00","og_image":[{"url":"http:\/\/vg02.met.vgwort.de\/na\/099a42723ed64bb19d9b294e6a753455","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"28\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/mnBeziehung_mit_Listenfeld_und_Datenblatt\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/mnBeziehung_mit_Listenfeld_und_Datenblatt\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"m:n-Beziehung mit Listenfeld und Datenblatt","datePublished":"2024-08-01T09:54:27+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/mnBeziehung_mit_Listenfeld_und_Datenblatt\/"},"wordCount":5131,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/mnBeziehung_mit_Listenfeld_und_Datenblatt\/#primaryimage"},"thumbnailUrl":"http:\/\/vg02.met.vgwort.de\/na\/099a42723ed64bb19d9b294e6a753455","articleSection":["2024","4\/2024","Mit Formularen arbeiten"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/mnBeziehung_mit_Listenfeld_und_Datenblatt\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/mnBeziehung_mit_Listenfeld_und_Datenblatt\/","url":"https:\/\/access-im-unternehmen.de\/mnBeziehung_mit_Listenfeld_und_Datenblatt\/","name":"m:n-Beziehung mit Listenfeld und Datenblatt - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/mnBeziehung_mit_Listenfeld_und_Datenblatt\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/mnBeziehung_mit_Listenfeld_und_Datenblatt\/#primaryimage"},"thumbnailUrl":"http:\/\/vg02.met.vgwort.de\/na\/099a42723ed64bb19d9b294e6a753455","datePublished":"2024-08-01T09:54:27+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/mnBeziehung_mit_Listenfeld_und_Datenblatt\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/mnBeziehung_mit_Listenfeld_und_Datenblatt\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/mnBeziehung_mit_Listenfeld_und_Datenblatt\/#primaryimage","url":"http:\/\/vg02.met.vgwort.de\/na\/099a42723ed64bb19d9b294e6a753455","contentUrl":"http:\/\/vg02.met.vgwort.de\/na\/099a42723ed64bb19d9b294e6a753455"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/mnBeziehung_mit_Listenfeld_und_Datenblatt\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"m:n-Beziehung mit Listenfeld und Datenblatt"}]},{"@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\/55001510","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=55001510"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001510\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001510"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001510"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001510"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}