{"id":55001408,"date":"2022-12-01T00:00:00","date_gmt":"2022-12-01T11:16:14","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1408"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Kunden_nach_bestellten_Produkten_filtern","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Kunden_nach_bestellten_Produkten_filtern\/","title":{"rendered":"Kunden nach bestellten Produkten filtern"},"content":{"rendered":"<p><b>Kunden nach bestellten Produkten kann jeder filtern, der sich ein wenig mit dem Abfrageentwurf besch&auml;ftigt hat. Etwas aufwendiger ist es schon, ein Formular zu erstellen, dass verschiedene M&ouml;glichkeiten zum Filtern von Kunden nach den bestellten Produkten bietet. Hier wollen wir beispielsweise ein Produkt ausw&auml;hlen, sodass direkt alle Kunden in einer Liste angezeigt werden, die dieses Produkt bestellt haben. Oder wir gehen noch einen Schritt weiter und wollen Kunden anzeigen, die mindestens eines von mehreren Produkten geordert haben. Um dann vielleicht noch solche Kunden auszuschlie&szlig;en, die bereits ein bestimmtes anderes Produkt besitzen. Also auf ins Abenteuer!<\/b><\/p>\n<h2>Warum Kunden nach Produkten filtern?<\/h2>\n<p>Bevor wir uns an die Arbeit dieses recht aufwendigen Unterfangens machen, wollen wir uns &uuml;berlegen, wozu wir das Ergebnis &uuml;berhaupt nutzen k&ouml;nnen. Mir als Shopbetreiber f&auml;llt da direkt ein, Kunden, die ein bestimmtes Produkt erworben haben, &uuml;ber eine neue Version dieses Produkts zu informieren. Oder man m&ouml;chte dem Kunden mitteilen, dass eine Lizenz ausl&auml;uft, damit er diese verl&auml;ngern kann. Vielleicht wollen wir auch einfach Kunden, die Produkt A bestellt haben, eine Empfehlung zu dem dazu passenden Produkt B geben. Dabei wollen wir dann nat&uuml;rlich nur Kunden anschreiben, die Produkt B noch nicht bestellt haben. Sie sehen: Es lohnt sich, die Kunden nach den bestellten Produkten selektieren zu k&ouml;nnen.<\/p>\n<h2>Voraussetzungen<\/h2>\n<p>Als Basis f&uuml;r die Ermittlung von Kunden nach den bestellten Produkten verwenden wir die Beispieldatenbank, die wir bereits in der Beitragsreihe zur Rechnungsverwaltung vorgestellt haben. Den ersten Teil dieser Beitragsreihe finden Sie &uuml;brigens unter dem Titel <b>Rechnungsverwaltung: Datenmodell <\/b>(<b>www.access-im-unternehmen.de\/1385<\/b>). <\/p>\n<p>Der relevante Teil des Datenmodells dieser Datenbank ist in Bild 1 abgebildet.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_06\/pic_1408_001.png\" alt=\"Tabellen, die am Filtern von Kunden nach Produkten beteiligt sind\" width=\"700\" height=\"318,2679\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Tabellen, die am Filtern von Kunden nach Produkten beteiligt sind<\/span><\/b><\/p>\n<h2>Geplante Funktionen f&uuml;r die L&ouml;sung<\/h2>\n<p>Wir wollen mit dem Formular zum Filtern von Kunden nach bestellten Produkten m&ouml;glichst flexibel arbeiten k&ouml;nnen.<\/p>\n<p>Also schauen wir uns zun&auml;chst an, welche Filterm&ouml;glichkeiten wir abbilden wollen:<\/p>\n<ul>\n<li>Filtern nach Kunden, die ein bestimmtes Produkt bestellt haben<\/li>\n<li>Filtern nach Kunden, die eines von mehreren Produkten bestellt haben (Oder-Verkn&uuml;pfung)<\/li>\n<li>Filtern nach Kunden, die ein bestimmtes Produkt nicht bestellt haben<\/li>\n<li>Filtern nach Kunden, die bestimmte Produkte nicht bestellt haben (Und-Verkn&uuml;pfung)<\/li>\n<\/ul>\n<p>Die ersten beiden Filterm&ouml;glichkeiten sollen au&szlig;erdem per Und-Verkn&uuml;pfung mit den letzten beiden M&ouml;glichkeiten kombiniert werden.<\/p>\n<h2>Einfache Auswahl der Produkte, nach denen gefiltert werden soll<\/h2>\n<p>Wir wollen zwei Listenfelder im Formular anzeigen, die unterschiedliche Produkte enthalten:<\/p>\n<ul>\n<li>Das erste Listenfeld soll die Produkte anzeigen, von denen der Kunde mindestens eines bestellt hat.<\/li>\n<li>Das zweite Listenfeld soll die Produkte anzeigen, die der Kunde nicht bestellt hat.<\/li>\n<\/ul>\n<p>Um die beiden Listenfelder zu f&uuml;llen, bieten wir ein drittes Listenfeld an, das alle Produkte enth&auml;lt. Aus diesem w&auml;hlen wir dann die Produkte aus, die den beiden zuvor genannten Listenfeldern hinzugef&uuml;gt werden sollen.<\/p>\n<p>Um aus diesem Listenfeld komfortabel die gew&uuml;nschten Produkte ausw&auml;hlen zu k&ouml;nnen, wollen wir &uuml;ber diesem Listenfeld noch ein Textfeld einf&uuml;gen, mit dem wir die angezeigten Produkte nach dem eingegebenen Text filtern k&ouml;nnen.<\/p>\n<p>Unter den Listenfeldern zur Auswahl der zu filternden Produkte f&uuml;gen wir schlie&szlig;lich noch ein Unterformular ein, das die Kunden anzeigt, welche die den Listenfeldern hinzugef&uuml;gten Produkte entweder bestellt haben &#8211; oder auch nicht.<\/p>\n<h2>Unterformular zur Anzeige der Kunden<\/h2>\n<p>Als Erstes legen wir das Unterformular <b>sfmKundenNachProduktenFiltern<\/b> an. Dieses soll die Kunden anzeigen, die den im Hauptformular festgelegten Kriterien entsprechen. Beim &Ouml;ffnen des Formulars jedoch soll es zun&auml;chst alle Kunden anzeigen. Deshalb stellen wir die Eigenschaft <b>Datensatzquelle<\/b> auf eine Abfrage ein, welche die Tabelle <b>tblKunden <\/b>als Datenherkunft verwendet und davon die Felder <b>KundeID<\/b>, <b>AnredeID<\/b>, <b>Nachname<\/b>, <b>Vorname <\/b>und <b>EMail <\/b>anzeigt, sortiert nach den Feldern <b>Nachname <\/b>und <b>Vorname<\/b> (siehe Bild 2).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_06\/pic_1408_002.png\" alt=\"Abfrage f&uuml;r das Unterformular\" width=\"499,5589\" height=\"342,9448\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Abfrage f&uuml;r das Unterformular<\/span><\/b><\/p>\n<p>Anschlie&szlig;end ziehen wir alle Felder dieser Abfrage aus der Feldliste in den Entwurf des Formulars (siehe Bild 3).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_06\/pic_1408_003.png\" alt=\"Entwurf des Unterformulars\" width=\"424,5589\" height=\"271,5408\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Entwurf des Unterformulars<\/span><\/b><\/p>\n<p>Damit dieses Formular seine Daten in der Datenblattansicht anzeigt, stellen wir seine Eigenschaft <b>Standardansicht <\/b>auf den Wert <b>Datenblatt <\/b>ein. Nun schlie&szlig;en und speichern wir das Unterformular.<\/p>\n<h2>Hauptformular zum Filtern der Kunden<\/h2>\n<p>Dann legen wir ein weiteres neues Formular namens <b>frmKundenNachProduktenFiltern <\/b>an und f&uuml;gen diesem folgende Steuerelemente hinzu:<\/p>\n<ul>\n<li>Textfeld <b>txtProduktfilter<\/b><\/li>\n<li>Listenfeld <b>lstAlleProdukte<\/b><\/li>\n<li>Listenfeld <b>lstBestellteProdukte<\/b><\/li>\n<li>Listenfeld <b>lstNichtBestellteProdukte<\/b><\/li>\n<li>Schaltfl&auml;che <b>cmdZuBestelltenProdukten<\/b><\/li>\n<li>Schaltfl&auml;che <b>cmdZuNichtBestelltenProdukten<\/b><\/li>\n<\/ul>\n<p>Au&szlig;erdem ziehen wir aus dem Navigationsbereich das Formular <b>sfmKundenNachProduktenFiltern <\/b>in das Hauptformular. Die Steuerelemente ordnen wir dabei wie in Bild 4 an.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_06\/pic_1408_004.png\" alt=\"Anordnung der Steuerelemente im Hauptformular\" width=\"574,559\" height=\"482,2191\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Anordnung der Steuerelemente im Hauptformular<\/span><\/b><\/p>\n<h2>Listenfelder mit Daten f&uuml;llen<\/h2>\n<p>Das Listenfeld <b>lstAlleProdukte <\/b>soll alle Produkte anzeigen mit Ausnahme derer, die bereits zu einem der Listenfelder <b>lstBestellteProdukte <\/b>oder <b>lstNichtBestellteProdukte <\/b>hinzugef&uuml;gt wurden.<\/p>\n<p>Das Listenfeld <b>lstBestellteProdukte <\/b>soll alle Produkte anzeigen, die nach Markierung im Listenfeld <b>lstAlleProdukte <\/b>mit der Schaltfl&auml;che <b>cmdZuBestelltenProdukten <\/b>hinzugef&uuml;gt wurden.<\/p>\n<p>Das Listenfeld <b>lstNichtBestellteProdukte <\/b>soll analog alle Produkte anzeigen, die nach Markierung im Listenfeld <b>lstAlleProdukte <\/b>mit der Schaltfl&auml;che <b>cmdZuNichtBestelltenProdukten <\/b>hinzugef&uuml;gt wurden.<\/p>\n<p>Nun m&uuml;ssen wir allerdings noch festlegen, wie wir die Produkte markieren, die in einem der beiden Listenfelder <b>lstBestellteProdukte <\/b>oder <b>lstNichtBestellteProdukte <\/b>angezeigt werden.<\/p>\n<h2>Tabellen zum Speichern der ein- und auszuschlie&szlig;enden Produkte<\/h2>\n<p>Wir k&ouml;nnten die beiden Listenfelder einfach mit dem Wert <b>Wertliste <\/b>f&uuml;r die Eigenschaft <b>Herkunftsart <\/b>ausstatten und die Produkte als String-Liste der Eigenschaft <b>Datensatzherkunft <\/b>hinzuf&uuml;gen. Wenn wir die Produkte in diesen beiden Listen allerdings jederzeit nach dem Produktnamen sortiert anzeigen wollen, haben wir mehr Programmieraufwand. Und da wir in Access arbeiten, legen wir schnell zwei Tabellen namens <b>tblBestellteProdukte <\/b>und <b>tblNichtBestellteProdukte <\/b>an. Die erste sieht in der Entwurfsansicht wie in Bild 5 aus und enth&auml;lt lediglich ein Zahlenfeld zum Speichern der <b>ProduktID<\/b>-Werte, nach denen die Kunden gefiltert werden sollen. Dieses Feld haben wir als Prim&auml;rschl&uuml;sselfeld definiert, jedoch logischerweise nicht mit dem Datentyp <b>Autowert<\/b>. Die zweite unterscheidet sich nur durch den Namen von der ersten Tabelle. Tipp zum Zeit sparen: Einfach die erste Tabelle kopieren und unter dem Namen <b>tblNichtBestellteProdukte <\/b>speichern.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_06\/pic_1408_005.png\" alt=\"Entwurf der Tabelle tblBestellteProdukte\" width=\"499,5589\" height=\"285,5886\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Entwurf der Tabelle tblBestellteProdukte<\/span><\/b><\/p>\n<h2>Datensatzherkunft der Listenfelder lstBestellteProdukte und lstNichtBestellteProdukte<\/h2>\n<p>Die Datensatzherkunft der beiden rechten Listenfelder k&ouml;nnen wir nun bereits einstellen. Die f&uuml;r das Listenfeld <b>lstBestellteProdukte <\/b>gestalten wir wie in Bild 6. Sie soll alle Datens&auml;tze der Tabelle <b>tblProdukte <\/b>anzeigen, deren Wert im Feld <b>ProduktID <\/b>in der Tabelle <b>tblBestellteProdukte <\/b>gespeichert ist. Nachdem Sie die beiden Tabellen zum Abfrageentwurf hinzugef&uuml;gt haben, m&uuml;ssen Sie die Beziehung zwischen den Feldern <b>ID <\/b>der Tabelle <b>tblProdukte <\/b>und <b>ProduktID <\/b>der Tabelle <b>tblBestellteProdukte <\/b>manuell hinzuf&uuml;gen. Diese Beziehung wollen wir nicht im Datenbankfenster festlegen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_06\/pic_1408_006.png\" alt=\"Datensatzherkunft des Listenfeldes lstBestellteProdukte\" width=\"424,5589\" height=\"351,3903\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Datensatzherkunft des Listenfeldes lstBestellteProdukte<\/span><\/b><\/p>\n<p>Die Datensatzherkunft f&uuml;r das Listenfeld <b>lstNichtBestellteProdukte <\/b>erstellen wir analog, diesmal verwenden wir jedoch die Tabelle <b>tblNichtBestellteProdukte <\/b>als zweite Tabelle.<\/p>\n<h2>Weitere Einstellungen f&uuml;r die Listenfelder<\/h2>\n<p>F&uuml;r alle drei Listenfelder nehmen wir nun die Format-Einstellungen vor. Hier legen wir f&uuml;r die Eigenschaft <b>Spaltenanzahl <\/b>den Wert <b>2 <\/b>und f&uuml;r <b>Spaltenbreiten <\/b>den Wert <b>0cm <\/b>fest. So zeigen die Listenfelder immer nur die Produktbezeichnung an, aber nicht den Wert der gebundenen Spalte <b>ProduktID<\/b>.<\/p>\n<h2>Daten des Listenfeldes lstAlleProdukte<\/h2>\n<p>Der Name dieses Listenfeldes ist eigentlich irref&uuml;hrend, denn es zeigt nur direkt nach dem &Ouml;ffnen des Formulars alle Produkte an. Sobald der Benutzer &uuml;ber das Textfeld <b>txtProduktfilter<\/b> einen Filter eingegeben hat oder eines der Produkte in eines der rechten Listenfelder verschoben hat, zeigt es nicht mehr alle Produkte an.<\/p>\n<p>Es soll dann nur noch die Produkte anzeigen, die nach dem Filtern und dem Entfernen der bereits in einem der &uuml;brigen Listenfeldern enthaltenen Produkte &uuml;brig bleiben.<\/p>\n<p>Zuerst aber soll dieses Listenfeld einfach alle Produkte anzeigen, die in der Tabelle <b>tblProdukte <\/b>enthalten sind &#8211; und zwar nach dem Alphabet sortiert. Dazu weisen wir der Eigenschaft <b>Datensatzherkunft <\/b>des Listenfeldes die Abfrage aus Bild 7 zu.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_06\/pic_1408_007.png\" alt=\"Datensatzherkunft des Listenfeldes lstAlleProdukte\" width=\"424,5589\" height=\"360,5595\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 7: Datensatzherkunft des Listenfeldes lstAlleProdukte<\/span><\/b><\/p>\n<p>Die enthaltenen Daten sollen anschlie&szlig;end nach verschiedenen Aktionen aktualisiert werden. Bei den Aktionen handelt es sich um die folgenden:<\/p>\n<ul>\n<li>Filtern &uuml;ber das Textfeld <b>txtProduktfilter<\/b> nach dem Ausl&ouml;sen des Ereignisses <b>Bei &Auml;nderung<\/b><\/li>\n<li>Hinzuf&uuml;gen eines der Produkte zu den Listenfeldern <b>lstBestellteProdukte <\/b>und <b>lstNichtBestellteProdukte mit den Schaltfl&auml;chen cmdZuBestelltenProdukten<\/b> und <b>cmdZuNichtBestelltenProdukten<\/b><\/li>\n<li>Entfernen eines der Produkte aus den Listenfeldern <b>lstBestellteProdukte <\/b>und <b>lstNichtBestellteProdukte<\/b> per Doppelklick auf einen der Eintr&auml;ge der Listenfelder<\/li>\n<\/ul>\n<p>Deshalb rufen wir von all den durch die oben beschriebenen Aktionen Prozeduren auf, welche die Datensatzherkunft des Listenfeldes <b>lstAlleProdukte <\/b>aktualisiert (und sp&auml;ter auch die Liste der Kunden).<\/p>\n<p>Diese Prozeduren sollen <b>ProdukteAktualisieren<\/b> und <b>KundenAktualisieren <\/b>hei&szlig;en.<\/p>\n<h2>Prozeduren zum Aktualisieren von Produkten und Kunden<\/h2>\n<p>Die Prozeduren <b>ProdukteAktualisieren<\/b> und <b>KundenAktualisieren <\/b>sollen den im Textfeld <b>txtProduktfilter <\/b>eingegebenen Suchbegriff ber&uuml;cksichtigen und zus&auml;tzlich &uuml;ber die in die beiden Tabellen <b>tblBestellteProdukte <\/b>und <b>tblNichtBestellteProdukte <\/b>gespeicherten Eintr&auml;ge die noch verf&uuml;gbaren Produkte im Listenfeld <b>lstAlleProdukte <\/b>anzeigen. Schlie&szlig;lich soll noch die Anzeige der Kunden passend zu den in den beiden Tabellen gespeicherten Daten aktualisiert werden.<\/p>\n<p><!--30percent--><\/p>\n<p>Da diese Prozeduren sowohl durch das Ereignis <b>Bei &Auml;nderung <\/b>des Textfeldes <b>txtProduktfilter <\/b>ausgel&ouml;st werden kann als auch durch das Hinzuf&uuml;gen oder Entfernen von Eintr&auml;gen zu den Tabellen <b>tblBestellteProdukte <\/b>und <b>tblNichtBestellteProdukte<\/b>, haben wir ein kleines Problem: Beim Aufruf &uuml;ber das Textfeld k&ouml;nnen wir auf den aktuellen Inhalt des Textfeldes nur &uuml;ber die Eigenschaft <b>Text <\/b>des Textfeldes zugreifen. Beim Aufruf &uuml;ber eines der anderen Ereignisse m&uuml;ssen wir die <b>Value<\/b>-Eigenschaft des Textfeldes verwenden.<\/p>\n<p>Wir k&ouml;nnen das auf verschiedene Arten l&ouml;sen. Eine ist, den Wert des Textfeldes jeweils in einer Variablen zu speichern, auf die wir dann von der Prozedur <b>ProdukteAktualisieren <\/b>aus zugreifen. Die zweite ist, dass wir den Inhalt der Variablen <b>Text <\/b>beim Aufruf vom Textfeld aus als Parameter &uuml;bergeben und beim Aufruf von den Schaltfl&auml;chen beziehungsweise Listenfeldern den Wert &uuml;ber die Eigenschaft <b>Value <\/b>ermitteln und dann ebenfalls per Parameter &uuml;bergeben.<\/p>\n<p>Da Variablen unter Umst&auml;nden ihren Wert verlieren k&ouml;nnen, nutzen wir die letztere Variante.<\/p>\n<h2>Nach Produktname filtern<\/h2>\n<p>Die Eingabe beziehungsweise &Auml;nderung des Inhalts des Textfeldes <b>txtProduktfilter <\/b>soll die im Listenfeld <b>lstAlleProdukte <\/b>enthaltenen Datens&auml;tze filtern. Dazu hinterlegen wir f&uuml;r die Ereigniseigenschaft <b>Bei &Auml;nderung <\/b>die folgende Prozedur:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>txtProduktfilter_Change()\r\n     ProdukteAktualisieren Me!txtProduktfilter.Text\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Diese ruft lediglich eine weitere Prozedur namens <b>ProdukteAktualisieren <\/b>auf und &uuml;bergibt dieser den aktuellen Inhalt des Textfeldes. Diese Prozedur beschreiben wir weiter unten. Erst schauen wir uns weitere Aufrufe dieser Prozedur an.<\/p>\n<h2>Hinzuf&uuml;gen von Produkten als einschlie&szlig;ende Kriterien f&uuml;r den Kundenfilter<\/h2>\n<p>Wenn der Benutzer wie in Bild 8 einen der Eintr&auml;ge des Listenfeldes <b>lstAlleProdukte <\/b>markiert und dann auf die Schaltfl&auml;che <b>cmdZuBestelltenProdukten <\/b>klickt, l&ouml;st dies das Ereignis <b>Beim Klicken <\/b>der Schaltfl&auml;che aus.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_06\/pic_1408_008.png\" alt=\"Hinzuf&uuml;gen eines Produkts zu den bestellten Produkten\" width=\"574,559\" height=\"258,7643\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 8: Hinzuf&uuml;gen eines Produkts zu den bestellten Produkten<\/span><\/b><\/p>\n<p>Die Prozedur gestalten wir wie in Listing 1. Sie pr&uuml;ft zun&auml;chst, ob &uuml;berhaupt ein Eintrag im Listenfeld <b>lstAlleProdukte <\/b>markiert ist. Falls ja, erstellt sie eine Referenz auf das aktuelle <b>Database<\/b>-Objekt und f&uuml;hrt damit eine <b>INSERT INTO<\/b>-Aktionsabfrage aus.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdZuBestelltenProdukten_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> IsNull(Me!lstAlleProdukte)<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n         db.Execute \"INSERT INTO tblBestellteProdukte(ProduktID) VALUES(\" & Me!lstAlleProdukte & \")\", dbFailOnError\r\n         ProdukteAktualisieren Nz(Me!txtProduktfilter)\r\n         KundenAktualisieren\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 1: Produkt zum Listenfeld der als Kriterien einzuschlie&szlig;enden Produkte hinzuf&uuml;gen<\/span><\/b><\/p>\n<p>Diese soll den Prim&auml;rschl&uuml;sselwert des aktuell im Listenfeld markierten Eintrags als neuen Datensatz in die Tabelle <b>tblBestellteProdukte <\/b>einf&uuml;gen. Anschlie&szlig;end erfolgt hier ebenfalls ein Aufruf der bereits weiter oben erw&auml;hnten Prozedur <b>ProdukteAktualisieren<\/b>, diesmal mit dem &uuml;ber die Eigenschaft <b>Value <\/b>des Textfeldes <b>txtProduktfilter <\/b>ermitteln Feldwert. Dass dieses Feld einen <b>NULL<\/b>-Wert enthalten kann, fangen wir mit der <b>Nz<\/b>-Funktion ab und &uuml;bergeben in diesem Fall eine leere Zeichenkette.<\/p>\n<p>Danach folgt noch ein Aufruf der Prozedur <b>KundenAktualisieren<\/b>. Diese erledigt den gr&ouml;&szlig;ten Teil der Arbeit, denn sie stellt die Datensatzquelle f&uuml;r das Datenblatt im Unterformular zusammen. Mehr dazu weiter unten.<\/p>\n<h2>Hinzuf&uuml;gen von Produkten als ausschlie&szlig;ende Kriterien f&uuml;r den Kundenfilter<\/h2>\n<p>Die Prozedur, die durch die weiter unten befindliche Schaltfl&auml;che namens <b>cmdZuNichtBestelltenProdukten <\/b>ausgel&ouml;st wird, funktioniert recht &auml;hnlich.<\/p>\n<p>Sie verwendet allerdings eine andere <b>INSERT INTO<\/b>-Anweisung und f&uuml;gt mit dieser den entsprechenden Datensatz zur Tabelle <b>tblNichtBestellteProdukte <\/b>ein (siehe Listing 2).<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdZuNichtBestelltenProdukten_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> IsNull(Me!lstAlleProdukte)<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n         db.Execute \"INSERT INTO tblNichtBestellteProdukte(ProduktID) VALUES(\" & Me!lstAlleProdukte & \")\", dbFailOnError\r\n         ProdukteAktualisieren Nz(Me!txtProduktfilter)\r\n         KundenAktualisieren\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: Produkt zum Listenfeld der auszuschlie&szlig;enden Produkte hinzuf&uuml;gen<\/span><\/b><\/p>\n<p>Auch diese Prozedur ruft anschlie&szlig;end die beiden Prozeduren <b>ProdukteAktualisieren <\/b>und <b>KundenAktualisieren <\/b>auf.<\/p>\n<h2>Entfernen von Produkten als einschlie&szlig;ende Kriterien f&uuml;r den Kundenfilter<\/h2>\n<p>Die n&auml;chste Prozedur wird ausgel&ouml;st, wenn der Benutzer einen Doppelklick auf einen der Eintr&auml;ge des Listenfeldes <b>lstBestellteProdukte <\/b>ausf&uuml;hrt (siehe Bild 9).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_06\/pic_1408_009.png\" alt=\"Doppelklick auf das Listenfeld lstBestellteProdukte\" width=\"574,559\" height=\"252,8059\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 9: Doppelklick auf das Listenfeld lstBestellteProdukte<\/span><\/b><\/p>\n<p>Dies f&uuml;hrt zum Aufruf der Prozedur aus Listing 3.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>lstBestellteProdukte_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;\">If <\/span><span style=\"color:blue;\">Not<\/span> IsNull(Me!lstBestellteProdukte)<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n         db.Execute \"DELETE FROM tblBestellteProdukte WHERE ProduktID = \" & Me!lstBestellteProdukte, dbFailOnError\r\n         ProdukteAktualisieren Nz(Me!txtProduktfilter)\r\n         KundenAktualisieren\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: Produkt aus dem Listenfeld der als Kriterien einzuschlie&szlig;enden Produkte entfernen<\/span><\/b><\/p>\n<p>Die Prozedur pr&uuml;ft wiederum, ob der Benutzer nicht auf eine leere Stelle im Listenfeld geklickt hat, und zwar mit der <b>IsNull<\/b>-Funktion.<\/p>\n<p>Ist <b>lstBestellteProdukte <\/b>nicht <b>NULL<\/b>, sorgt die Prozedur daf&uuml;r, dass der angeklickte Eintrag aus der Tabelle <b>tblBestellteProdukte <\/b>gel&ouml;scht wird.<\/p>\n<p>Dazu nutzt sie die <b>Execute<\/b>-Methode des aktuellen <b>Database<\/b>-Objekts und &uuml;bergibt dieser eine entsprechende <b>DELETE<\/b>-Anweisung.<\/p>\n<p>Anschlie&szlig;end folgen die obligatorischen Aufrufe der Prozeduren <b>ProdukteAktualisieren <\/b>und <b>KundenAktualisieren<\/b>.<\/p>\n<h2>Entfernen von Produkten als ausschlie&szlig;ende Kriterien f&uuml;r den Kundenfilter<\/h2>\n<p>Schlie&szlig;lich fehlt noch die Prozedur, die durch einen Doppelklick auf das Listenfeld <b>lstNichtBestellteProdukte <\/b>ausgel&ouml;st wird (siehe Listing 4).<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>lstNichtBestellteProdukte_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;\">If <\/span><span style=\"color:blue;\">Not<\/span> IsNull(Me!lstNichtBestellteProdukte)<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n         db.Execute \"DELETE FROM tblNichtBestellteProdukte WHERE ProduktID = \" & Me!lstNichtBestellteProdukte, dbFailOnError\r\n         ProdukteAktualisieren Nz(Me!txtProduktfilter)\r\n         KundenAktualisieren\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: Produkt aus dem Listenfeld der als Kriterien auszuschlie&szlig;enden Produkte entfernen<\/span><\/b><\/p>\n<p>Diese f&uuml;hrt &auml;hnliche Anweisungen wie die zuvor beschriebene Prozedur aus, entfernt aber einen Datensatz aus der Tabelle <b>tblNichtBestellteProdukte<\/b>.<\/p>\n<h2>Aktualisieren der Produkte im Listenfeld lstAlleProdukte<\/h2>\n<p>Damit kommen wir zur Prozedur <b>ProdukteAktualisieren<\/b> (siehe Listing 5). Diese wird immer aufgerufen, wenn wir eine der bisherigen f&uuml;nf Prozeduren per Ereignis ausgel&ouml;st haben &#8211; also durch das &Auml;ndern des Produktfilters oder durch Hinzuf&uuml;gen oder Entfernen eines Produkts aus einem der beiden Listenfelder <b>lstBestellteProdukte <\/b>oder <b>lstNichtBestellteProdukte<\/b>.<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>ProdukteAktualisieren(strProduktfilter<span style=\"color:blue;\"> As String<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>strSQL<span style=\"color:blue;\"> As String<\/span>\r\n     strSQL = \"SELECT ID, Produktbezeichnung FROM tblProdukte\" _\r\n            & \" WHERE ID NOT IN (SELECT ProduktID FROM tblBestellteProdukte)\" _\r\n            & \" AND ID NOT IN (SELECT ProduktID FROM tblNichtBestellteProdukte)\"\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strProduktfilter) &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n        strSQL = strSQL & \" AND Produktbezeichnung LIKE ''*\" & strProduktfilter & \"*''\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Debug.Print<\/span> strSQL\r\n     Me!lstAlleProdukte.RowSource = strSQL\r\n     Me!lstBestellteProdukte.Requery\r\n     Me!lstNichtBestellteProdukte.Requery\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 5: Produkte in der Liste aller nicht zugeordneten Produkte aktualisieren<\/span><\/b><\/p>\n<p>Die Prozedur nimmt mit dem Parameter <b>strProduktfilter <\/b>die im Textfeld <b>txtProduktfilter <\/b>enthaltene Zeichenkette als Filter entgegen.<\/p>\n<p>In einer Stringvariablen namens <b>strSQL <\/b>stellt sie zun&auml;chst die Basisabfrage zusammen:<\/p>\n<pre>SELECT ID, Produktbezeichnung FROM tblProdukte<\/pre>\n<p>Dann pr&uuml;ft sie, ob der Parameter <b>strProduktfilter<\/b>, als der Wert aus dem Textfeld <b>txtProduktfilter<\/b>, eine Zeichenkette mit mindestens einem Zeichen enth&auml;lt.<\/p>\n<p>Ist das der Fall, f&uuml;gt sie einer weiteren Stringvariablen namens <b>strWhere <\/b>ein erstes Kriterium hinzu, das beispielsweise bei dem Wert <b>Access <\/b>im Textfeld <b>txtProduktfilter <\/b>wie folgt aussieht:<\/p>\n<pre>  AND Produktbezeichnung LIKE ''*Access*''<\/pre>\n<p>Dann f&uuml;gt sie dieser Zeichenkette noch zwei weitere Kriterien hinzu.<\/p>\n<p>Das erste schlie&szlig;t alle Produkte aus, deren Prim&auml;rschl&uuml;sselwerte in der Tabelle <b>tblBestellteProdukte <\/b>gespeichert sind:<\/p>\n<pre>  AND ID NOT IN (SELECT ProduktID FROM tblBestellteProdukte)<\/pre>\n<p>Das n&auml;chste Kriterium erledigt das Gleiche f&uuml;r die Eintr&auml;ge der Tabelle <b>tblNichtBestellteProdukte<\/b>:<\/p>\n<pre>  AND ID NOT IN (SELECT ProduktID FROM tblNichtBestellteProdukte)<\/pre>\n<p>Danach entfernt sie, da <b>strWhere <\/b>nun auf jeden Fall mit einem Leerzeichen und der Zeichenkette <b>AND <\/b>beginnt, die ersten vier Zeichen aus <b>strWhere <\/b>und h&auml;ngt den verbleibenden Inhalt von <b>strWhere <\/b>neben dem Schl&uuml;sselwort <b>WHERE <\/b>an den Inhalt von <b>strSQL <\/b>an. Damit entsteht nun eine Abfrage wie die folgende:<\/p>\n<pre>SELECT ID, Produktbezeichnung FROM tblProdukte WHERE Produktbezeichnung LIKE ''*Access*'' AND ID NOT IN (SELECT ProduktID FROM tblBestellteProdukte) AND ID NOT IN (SELECT ProduktID FROM tblNichtBestellteProdukte)<\/pre>\n<p>Diesen Ausdruck weist die Prozedur nun dem Listenfeld <b>lstAlleProdukte <\/b>zu. Au&szlig;erdem aktualisiert sie die Listenfelder <b>lstBestellteProdukte <\/b>und <b>lstNichtBestellteProdukte<\/b>.<\/p>\n<p>Damit zeigt das Listenfeld <b>lstAlleProdukte <\/b>nur noch diejenigen Datens&auml;tze an, die nicht durch den Produktfilter aus dem Textfeld <b>txtProduktfilter <\/b>ausgeschlossen sind oder in einem der beiden Listenfelder <b>lstBestellteProdukte <\/b>oder <b>lstNichtBestellteProdukte <\/b>angezeigt werden.<\/p>\n<h2>Aktualisieren des Unterformulars zur Anzeige der Kunden<\/h2>\n<p>Die weitaus kompliziertere Aufgabe ist das Zusammenstellen der Datensatzquelle f&uuml;r das Unterformular <b>sfmKundenNachProduktenFiltern<\/b>. Diese Aufgabe erledigt die Prozedur <b>KundenAktualisieren<\/b>, die beim Hinzuf&uuml;gen oder Entfernen eines der Produkte aus den beiden Listenfeldern <b>lstBestellteProdukte <\/b>oder <b>lstNichtBestellteProdukte <\/b>aufgerufen wird (siehe Listing 6).<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>KundenAktualisieren()\r\n     <span style=\"color:blue;\">Dim <\/span>strSQL<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strWhere<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>rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     <span style=\"color:blue;\">Set<\/span> rst = db.OpenRecordset(\"SELECT ProduktID FROM tblNichtBestellteProdukte\", dbOpenDynaset)\r\n     strSQL = \"SELECT DISTINCT ID, AnredeID, Nachname, Vorname, EMail FROM tblKunden\"\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> Me!lstBestellteProdukte.ListCount = 0<span style=\"color:blue;\"> Then<\/span>\r\n         strWhere = \" AND ID IN (SELECT tblBestellungen.KundeID FROM tblBestellungen INNER JOIN tblBestellpositionen \" _\r\n             & \"ON tblBestellungen.ID = tblBestellpositionen.BestellungID WHERE tblBestellpositionen.ProduktID IN \" _\r\n             & \"(SELECT ProduktID FROM tblBestellteProdukte))\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rst.EOF\r\n         strWhere = strWhere & \" AND ID NOT IN (SELECT tblBestellungen.KundeID FROM tblBestellungen \" _\r\n             & \"INNER JOIN tblBestellpositionen ON tblBestellungen.ID = tblBestellpositionen.BestellungID \" _\r\n             & \"WHERE tblBestellpositionen.ProduktID = \" & rst!ProduktID & \")\"\r\n         rst.Move<span style=\"color:blue;\">Next<\/span>\r\n     <span style=\"color:blue;\">Loop<\/span>\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> <span style=\"color:blue;\">Len<\/span>(strWhere) = 0<span style=\"color:blue;\"> Then<\/span>\r\n         strWhere = <span style=\"color:blue;\">Mid<\/span>(strWhere, 5)\r\n         strSQL = strSQL & \" WHERE \" & strWhere\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     Me!sfmKundenNachProduktenFiltern.Form.RecordSource = strSQL\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 6: Kunden nach den in den Steuerelementen festgelegten Kriterien filtern<\/span><\/b><\/p>\n<p>Die Prozedur schreibt zuerst den ersten Teil der als Datensatzquelle zu verwendenden Abfrage in die Variable <b>strSQL<\/b>. Sie lautet:<\/p>\n<pre>SELECT DISTINCT ID, AnredeID, Nachname, Vorname, EMail FROM tblKunden<\/pre>\n<p>Danach pr&uuml;ft sie, ob das Listenfeld <b>lstBestellteProdukte <\/b>mindestens einen Eintrag enth&auml;lt. Ist das der Fall, f&uuml;gt sie zu einer bis dahin noch leeren Zeichenkette in der Variablen <b>strWhere <\/b>einen Ausdruck hinzu, der wie folgt lautet:<\/p>\n<pre>AND ID IN (\r\n   SELECT tblBestellungen.KundeID \r\n   FROM tblBestellungen \r\n   INNER JOIN tblBestellpositionen \r\n   ON tblBestellungen.ID = tblBestellpositionen.BestellungID \r\n   WHERE tblBestellpositionen.ProduktID IN (\r\n     SELECT ProduktID FROM tblBestellteProdukte\r\n   )\r\n)<\/pre>\n<p>Der Ausdruck enth&auml;lt einen Kriteriumausdruck, der alle Prim&auml;rschl&uuml;sselwerte derjenigen Datens&auml;tze der Tabelle <b>tblBestellungen <\/b>ermittelt, die mit einem Datensatz der Tabelle <b>tblBestellpositionen verkn&uuml;pft <\/b>sind, der im Feld <b>ProduktID <\/b>eines der Produkte enth&auml;lt, die im Listenfeld <b>lstBestellteProdukte <\/b>beziehungsweise der Tabelle <b>tblBestellteProdukte <\/b>gespeichert sind. Letztere holen wir wieder als Vergleichswert eines Vergleichsausdrucks mit dem <b>IN<\/b>-Operator.<\/p>\n<h2>Kriterium f&uuml;r nicht bestellte Produkte zusammenstellen<\/h2>\n<p>Anschlie&szlig;end wird es etwas spannender. W&auml;hrend wir mit dem zuvor definierten Kriterium lediglich pr&uuml;fen, ob der Kunde irgendeines der Produkte aus dem Listenfeld <b>lstBestellteProdukte <\/b>geordert hat, m&uuml;ssen wir nun sicherstellen, dass er keines der auszuschlie&szlig;enden Produkte gekauft hat. Dazu reicht es nicht aus, diese wie zuvor mit einer <b>IN<\/b>-Klausel zu pr&uuml;fen &#8211; wir m&uuml;ssen nun alle auszuschlie&szlig;enden Produkte untersuchen.<\/p>\n<p>Dazu stellen wir eine ganze Reihe von Ausdr&uuml;cken zusammen, die alle per <b>AND<\/b>-Schl&uuml;sselwort verkn&uuml;pft werden. Dazu erstellt die Prozedur ein Recordset auf Basis der Tabelle <b>tblNichtBestellteProdukte<\/b>.<\/p>\n<p>Die enthaltenen Datens&auml;tze durchl&auml;uft die Prozedur anschlie&szlig;end in einer <b>Do While<\/b>-Schleife. Darin f&uuml;gt sie f&uuml;r jeden Datensatz ein neues Kriterium zur Zeichenkette aus <b>strWhere <\/b>hinzu.<\/p>\n<p>Hier stellen wir also nun f&uuml;r jedes auszuschlie&szlig;ende Produkt ein Kriterium zusammen, das die <b>KundeID<\/b>-Werte der Tabelle <b>tblBestellungen <\/b>liefert, denen &uuml;ber die Tabelle <b>tblBestellpositionen <\/b>eine der auszuschlie&szlig;enden <b>ProduktID<\/b>-Werte zugewiesen wurde. Und diese sollen mit der &uuml;bergeordneten Abfrage genau nicht geliefert werden, also negieren wir die Bedingung mit <b>ID NOT IN (&#8230;)<\/b> &#8211; hier am Beispiel der <b>ProduktID <\/b>mit dem Wert <b>123<\/b>:<\/p>\n<pre>AND ID NOT IN (\r\n   SELECT tblBestellungen.KundeID \r\n   FROM tblBestellungen \r\n   INNER JOIN tblBestellpositionen \r\n   ON tblBestellungen.ID = tblBestellpositionen.BestellungID \r\n   WHERE tblBestellpositionen.ProduktID = 123\r\n)<\/pre>\n<p>Nach dem durchlaufen aller Datens&auml;tze haben wir das Kriterium in <b>strWhere <\/b>zusammengestellt und k&ouml;nnen das f&uuml;hrende <b>AND<\/b>-Schl&uuml;sselwort durch <b>WHERE <\/b>ersetzen. Au&szlig;erdem h&auml;ngen wir den gesamten Ausdruck an die Abfrage aus <b>strSQL <\/b>an.<\/p>\n<p>Schlie&szlig;lich weisen wir die resultierende Abfrage der Eigenschaft <b>RecordSource <\/b>des Formulars im Unterformular-Steuerelement zu.<\/p>\n<h2>Filter ausprobieren<\/h2>\n<p>Danach k&ouml;nnen wir den Filter ausprobieren. Um alle Kunden zu finden, die zwar das Magazin <b>Access im Unternehmen <\/b>abonniert, aber noch keines der im unteren Listenfeld aufgef&uuml;hrten B&uuml;cher gekauft haben, stellen wir die Listenfelder wie in Bild 10 ein und erhalten das entsprechende Ergebnis.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2022_06\/pic_1408_010.png\" alt=\"Gefilterte Datens&auml;tze\" width=\"574,559\" height=\"447,4595\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 10: Gefilterte Datens&auml;tze<\/span><\/b><\/p>\n<h2>Kriterien l&ouml;schen oder behalten?<\/h2>\n<p>Die Inhalte der beiden Listenfelder <b>lstBestellteProdukte <\/b>und <b>lstNichtBestellteProdukte <\/b>werden in den Tabellen <b>tblBestellteProdukte <\/b>und <b>tblNichtBestellteProdukte <\/b>gespeichert.<\/p>\n<p>Wir m&uuml;ssen uns noch &uuml;berlegen, ob wir die so gespeicherten Kriterien beibehalten wollen, wenn der Benutzer das Formular schlie&szlig;t. So k&ouml;nnen wir die zuletzt getroffenen Kriterien beim &Ouml;ffnen des Formulars wiederherstellen. Alternativ leeren wir die Kriterien mit jedem Schlie&szlig;en-Vorgang.<\/p>\n<p>Wir entscheiden uns f&uuml;r eine L&ouml;sung, die das schnelle leeren der Kriterien-Listenfelder erlaubt und mit der wir, falls der Benutzer die Kriterien nicht l&ouml;scht, die Kriterien beim &Ouml;ffnen wiederherstellen k&ouml;nnten.<\/p>\n<h2>Kriterien und Auswahl beim &Ouml;ffnen wiederherstellen<\/h2>\n<p>Damit die nicht gel&ouml;schten Kriterien beim n&auml;chsten &Ouml;ffnen des Formulars wiederhergestellt werden, brauchen wir nur beim &Ouml;ffnen die beiden Prozeduren <b>ProdukteAktualisieren <\/b>und <b>KundenAktualisieren <\/b>aufzurufen. Das erledigen wir in der durch das Ereignis <b>Beim Laden <\/b>ausgel&ouml;sten Ereignisprozedur. Der Prozedur <b>ProdukteAktualisieren <\/b>&uuml;bergeben wir eine leere Zeichenkette, da das Textfeld <b>txtProduktfilter <\/b>zu diesem Zeitpunkt ohnehin noch leer ist:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>Form_Load()\r\n     ProdukteAktualisieren \"\"\r\n     KundenAktualisieren\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Auswahl in den Listenfeldern aufheben<\/h2>\n<p>Fehlt noch die Schaltfl&auml;che, mit welcher der Benutzer die beiden Listenfeldern <b>lstBestellteProdukte <\/b>und <b>lstNichtBestellteProdukte <\/b>leeren kann.<\/p>\n<p>Diese erstellt wieder eine Referenz auf das aktuelle <b>Database<\/b>-Objekt und nutzt dieses, um mit der <b>Execute<\/b>-Methode zwei <b>DELETE<\/b>-Abfragen zum Leeren der Tabellen <b>tblBestellteProdukte <\/b>und <b>tblNichtBestellteProdukte <\/b>abzusetzen. Au&szlig;erdem leert sie das Textfeld <b>txtProduktfilter <\/b>und aktualisiert anschlie&szlig;end per Aufruf der beiden Prozeduren <b>ProdukteAktualisieren <\/b>und <b>KundenAktualisieren <\/b>die Inhalte der Listenfelder und des Unterformulars:<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>cmdAllesZuruecksetzen_Click()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     db.Execute \"DELETE FROM tblBestellteProdukte\",  dbFailOnError\r\n     db.Execute \"DELETE FROM tblNichtBestellteProdukte\",  dbFailOnError\r\n     Me!txtProduktfilter = \"\"\r\n     ProdukteAktualisieren \"\"\r\n     KundenAktualisieren\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Letzte Arbeiten<\/h2>\n<p>Das Hauptformular zeigt aktuell noch Datensatzmarkierer et cetera an. Daher stellen wir die Eigenschaften <b>Datensatzmarkierer<\/b>, <b>Navigationsschaltfl&auml;chen<\/b>, <b>Bildlaufleisten <\/b>und <b>Trennlinien <\/b>auf den Wert <b>Nein <\/b>ein sowie <b>Automatisch zentrieren <\/b>auf den Wert <b>Ja<\/b>.<\/p>\n<p>Gegebenenfalls stellt man noch den Wert der beiden Eigenschaften <b>Horizontaler Anker <\/b>und <b>Vertikaler Anker <\/b>auf den Wert <b>Beide <\/b>ein, damit das Unterformular beim Vergr&ouml;&szlig;ern des Hauptformulars ebenfalls vergr&ouml;&szlig;ert wird.<\/p>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Mit der hier vorgestellten L&ouml;sung k&ouml;nnen Sie die Kunden herausfinden, die kein, ein oder mehrere Produkte bestellt haben und kein, ein oder mehrere Produkte noch nicht bestellt haben. Damit lassen sich gezielt Listen von Kunden ermitteln, denen man ein bestimmtes Produkt vorstellen m&ouml;chte.<\/p>\n<p>Eine denkbare Erweiterung ist eine Schaltfl&auml;che, mit der sich die Daten in eine neue Tabelle exportieren lassen oder auch in eine <b>.csv<\/b>-Datei. Von dort aus kann man die Daten dann weiterverarbeiten.<\/p>\n<h2>Downloads zu diesem Beitrag<\/h2>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>KundenNachBestelltenProdukten.accdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/9D60A3DD-A571-483A-AF50-801141AAA5A1\/aiu_1408.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Kunden nach bestellten Produkten kann jeder filtern, der sich ein wenig mit dem Abfrageentwurf besch&auml;ftigt hat. Etwas aufwendiger ist es schon, ein Formular zu erstellen, dass verschiedene M&ouml;glichkeiten zum Filtern von Kunden nach den bestellten Produkten bietet. Hier wollen wir beispielsweise ein Produkt ausw&auml;hlen, sodass direkt alle Kunden in einer Liste angezeigt werden, die dieses Produkt bestellt haben. Oder wir gehen noch einen Schritt weiter und wollen Kunden anzeigen, die mindestens eines von mehreren Produkten geordert haben. Um dann vielleicht noch solche Kunden auszuschlie&szlig;en, die bereits ein bestimmtes anderes Produkt besitzen. Also auf ins Abenteuer!<\/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":[662022,66062022,44000023],"tags":[],"class_list":["post-55001408","post","type-post","status-publish","format-standard","hentry","category-662022","category-66062022","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>Kunden nach bestellten Produkten filtern - 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\/Kunden_nach_bestellten_Produkten_filtern\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Kunden nach bestellten Produkten filtern\" \/>\n<meta property=\"og:description\" content=\"Kunden nach bestellten Produkten kann jeder filtern, der sich ein wenig mit dem Abfrageentwurf besch&auml;ftigt hat. Etwas aufwendiger ist es schon, ein Formular zu erstellen, dass verschiedene M&ouml;glichkeiten zum Filtern von Kunden nach den bestellten Produkten bietet. Hier wollen wir beispielsweise ein Produkt ausw&auml;hlen, sodass direkt alle Kunden in einer Liste angezeigt werden, die dieses Produkt bestellt haben. Oder wir gehen noch einen Schritt weiter und wollen Kunden anzeigen, die mindestens eines von mehreren Produkten geordert haben. Um dann vielleicht noch solche Kunden auszuschlie&szlig;en, die bereits ein bestimmtes anderes Produkt besitzen. Also auf ins Abenteuer!\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Kunden_nach_bestellten_Produkten_filtern\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2022-12-01T11:16:14+00:00\" \/>\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=\"20\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Kunden_nach_bestellten_Produkten_filtern\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Kunden_nach_bestellten_Produkten_filtern\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Kunden nach bestellten Produkten filtern\",\"datePublished\":\"2022-12-01T11:16:14+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Kunden_nach_bestellten_Produkten_filtern\\\/\"},\"wordCount\":3546,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"articleSection\":[\"2022\",\"6\\\/2022\",\"Mit Formularen arbeiten\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Kunden_nach_bestellten_Produkten_filtern\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Kunden_nach_bestellten_Produkten_filtern\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Kunden_nach_bestellten_Produkten_filtern\\\/\",\"name\":\"Kunden nach bestellten Produkten filtern - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"datePublished\":\"2022-12-01T11:16:14+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Kunden_nach_bestellten_Produkten_filtern\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Kunden_nach_bestellten_Produkten_filtern\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Kunden_nach_bestellten_Produkten_filtern\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Kunden nach bestellten Produkten filtern\"}]},{\"@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":"Kunden nach bestellten Produkten filtern - 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\/Kunden_nach_bestellten_Produkten_filtern\/","og_locale":"de_DE","og_type":"article","og_title":"Kunden nach bestellten Produkten filtern","og_description":"Kunden nach bestellten Produkten kann jeder filtern, der sich ein wenig mit dem Abfrageentwurf besch&auml;ftigt hat. Etwas aufwendiger ist es schon, ein Formular zu erstellen, dass verschiedene M&ouml;glichkeiten zum Filtern von Kunden nach den bestellten Produkten bietet. Hier wollen wir beispielsweise ein Produkt ausw&auml;hlen, sodass direkt alle Kunden in einer Liste angezeigt werden, die dieses Produkt bestellt haben. Oder wir gehen noch einen Schritt weiter und wollen Kunden anzeigen, die mindestens eines von mehreren Produkten geordert haben. Um dann vielleicht noch solche Kunden auszuschlie&szlig;en, die bereits ein bestimmtes anderes Produkt besitzen. Also auf ins Abenteuer!","og_url":"https:\/\/access-im-unternehmen.de\/Kunden_nach_bestellten_Produkten_filtern\/","og_site_name":"Access im Unternehmen","article_published_time":"2022-12-01T11:16:14+00:00","author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"20\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/Kunden_nach_bestellten_Produkten_filtern\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Kunden_nach_bestellten_Produkten_filtern\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Kunden nach bestellten Produkten filtern","datePublished":"2022-12-01T11:16:14+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Kunden_nach_bestellten_Produkten_filtern\/"},"wordCount":3546,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"articleSection":["2022","6\/2022","Mit Formularen arbeiten"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Kunden_nach_bestellten_Produkten_filtern\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Kunden_nach_bestellten_Produkten_filtern\/","url":"https:\/\/access-im-unternehmen.de\/Kunden_nach_bestellten_Produkten_filtern\/","name":"Kunden nach bestellten Produkten filtern - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"datePublished":"2022-12-01T11:16:14+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Kunden_nach_bestellten_Produkten_filtern\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Kunden_nach_bestellten_Produkten_filtern\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Kunden_nach_bestellten_Produkten_filtern\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Kunden nach bestellten Produkten filtern"}]},{"@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\/55001408","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=55001408"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001408\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001408"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001408"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001408"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}