André Minhorst, Duisburg
Verwalten von Rezepten – das hört sich gar nicht nach einer Musterlösung für Unternehmen an. Oder sind nur Hotelköche angesprochen Nein. Neben dem Anspruch, ein Muster für eine Vielzahl von Anwendungen zu bieten, soll die vorliegende Musterlösung auch von einer Vielzahl von Anwendern auf ihre eigenen Bedürfnisse zugeschnitten werden können.
Schließlich gibt es zu vielen Tätigkeiten “Rezepte” bzw. Verfahrensvorschriften, deren Durchführung auch noch die Verwendung bestimmter Zutaten – z. B. bestimmter Formulare – erfordert.
Neben der Verwaltung von Rezepten, Zutaten usw. werden Sie hier mit der Programmierung einer Schnittstelle konfrontiert. Diese dient dem Einlesen von fertigen Rezepten, von denen es im Internet Tausende gibt.
Wenn Sie sich z. B. einmal unter dem Stichwort Meal-Master die in Google registrierten deutschsprachigen Seiten ausgeben lassen, finden Sie zurzeit etwa 1.200 Einträge. Von vielen Seiten können Sie Dateien herunterladen, die teilweise hunderte von Rezepten enthalten.
Meal-Master ist ein amerikanisches Programm zur Verwaltung von Rezepten, das ein international anerkanntes Format für den Austausch von Rezepten enthält – die meisten verfügbaren Rezeptverwaltungen besitzen eine Funktion für den Import dieses Formats.
In diesem Zusammenhang lernen Sie auch, wie Sie mit einer Schnittstelle Daten von anderen Anwendungen in Ihre Datenbank importieren können. Der hier vorgestellte Vorgang lässt sich allerdings nicht mit denjenigen Assistenten vergleichen, die beispielsweise den Import von Daten aus Excel- oder CSV-Dateien erleichtern. Vielmehr zeigt dieser Beitrag, wie Daten in eine komplexe Datenstruktur eingearbeitet werden können.
Die Rezeptverwaltung soll neben ihrer eigentlichen Aufgabe, der Verwaltung von Rezepten, auch den Import und Export im Meal-Master-Format erlauben. Daher berücksichtigt das Datenmodell auch dessen weltweit gültiges Format, das in den folgenden Abschnitten kurz vorgestellt werden soll.
Das Meal-Master-Format
Meal-Master verwendet herkömmliche Textdateien. Es gibt eine genaue Spezifikation zur Festlegung der einzelnen Abschnitte eines Rezeptes. Ein Rezept besteht demnach aus den folgenden Angaben:
Aus diesen Informationen können Sie das Datenmodell zur Abbildung der Rezepte in einem relationalen Datenbanksystem – wie es Access ist – direkt ableiten.
Bild 1: Datenmodell der Rezeptverwaltung
Das Rezept als Hauptobjekt des Datenmodells benötigt eine eigene Tabelle zur Speicherung der rezeptspezifischen Daten. In diese Tabelle gehören der Rezeptname, die Anzahl der Portionen und die Anleitung – Informationen, die für jedes Rezept genau einmal auftauchen.
Anders verhält es sich mit den Kategorien und Zutaten: Jedes Rezept kann einer oder mehreren Kategorien zugeordnet werden und für jedes Rezept benötigen Sie in der Regel mehrere Zutaten.
Das gilt in beiden Fällen sicher auch umgekehrt: Sie ordnen jeder Kategorie ein oder mehrere Rezepte zu und auch jede Zutat taucht in einem oder in mehreren Rezepten auf.
Sie benötigen also zwei m:n-Beziehungen – sowohl zwischen Rezepten und Kategorien als auch zwischen Rezepten und Zutaten.
Die erste Beziehung realisieren Sie mit einer einfachen Verknüpfungstabelle. Die zweite zwischen Rezepten und Zutaten ist etwas komplizierter. Hier müssen Sie zusätzlich noch Informationen über Einheit und Anzahl der Zutaten hinzufügen.
Da diese Informationen sich unter Umständen bei jeder Kombination aus Rezepten und Zutaten unterscheiden, können Sie diese nur in der Verknüpfungstabelle zwischen den Tabellen der Rezepte und der Zutaten unterbringen.
Die Tabelle tblRezepte
Der Rezeptname, die Anleitung und die Anzahl der Portionen für die angegebenen Zutatenmengen speichern Sie in einer Tabelle namens tblRezepte. Da die Anleitung vermutlich oft länger als 255 Zeichen ist, verwenden Sie für diese Information ein Memo-Feld.
Die Tabelle tblZutaten
Alle in der Datenbank verwendeten Zutaten speichern Sie in der Tabelle tblZutaten. Neben der Bezeichnung der Zutat enthält die Tabelle ein Bemerkungsfeld.
Die Tabelle tblKategorien
Die Tabelle tblKategorien dient dem Speichern der Kategorien. Auch hier ist ein Bemerkungsfeld vorgesehen.
Die Tabelle tblEinheiten
Da es im Meal-Master-Format fest vorgegebene Einheiten gibt, die immer wieder auftauchen, sollen sie in einer separaten Tabelle gespeichert werden. Da die Bezeichnungen sich der Herkunft des Formats entsprechend auf amerikanische Einheiten beziehen, soll die Tabelle neben der englischen Bezeichnung auch noch eine deutsche Bezeichnung für die Ausgabe enthalten.
Die TabelletblRezepteZutaten
Diese Tabelle speichert alle Zuordnungen zwischen Rezepten und Zutaten. Zusätzlich soll die Tabelle zu jeder Zuordnung die Einheit, die Anzahl der Einheiten und Bemerkungen speichern. Die Einheiten werden als Zahlenwert eingegeben, der sich auf die verknüpfte Tabelle tblEinheiten bezieht.
Bild 2: Beispiel eines Rezeptes im Meal-Master-Format
Die beiden Felder RezeptID und ZutatID dienen als Fremdschlüsselfelder zu den entsprechenden Tabellen. Sie bilden allerdings keinen gemeinsamen Primärschlüssel.
Die Tabelle tblRezepteKategorien
Die Tabelle zur Herstellung der Beziehung zwischen Rezepten und Kategorien ist eine einfache Verknüpfungstabelle mit den Fremdschlüsselfeldern RezeptID und KategorieID zu den Tabellen tblRezepte und tblKategorien.
Die Beziehungen
Die Beschreibung der verwendeten Tabellen enthält bereits die wichtigsten Informationen zu den Beziehungen zwischen den Tabellen. Bild 1 zeigt eine Zusammenfassung der Tabellen und der Beziehungen zwischen den Tabellen. Für alle Beziehungen soll referentielle Integrität bestehen.
Die Eingabe von Test- und anderen Daten ist in der Regel wenig unterhaltsam – zumindest, wenn Sie die Daten stur von einer Vorlage übernehmen müssen.
Daher sorgen Sie nun zunächst einmal für eine Schnittstelle zum Import von bestehenden Daten im Meal-Master-Format.
Hinweis
Zwar ist auch das Erstellen von Schnittstellen nicht gerade beflügelnd, aber deren anschließende Anwendung und die daraus resultierende Zeitersparnis ist ein guter Grund, diese Arbeit in Kauf zu nehmen.
Das Import-Format
Die Dateien mit den Meal-Master-Rezepten sind reine Textdateien. Glücklicherweise haben Sie eine einigermaßen regelmäßige Struktur – was bei einem weltweit verwendeten Format vorausgesetzt werden kann. Die Frage, warum ein solch verbreitetes Format nicht auf dem Stand der Zeit ist – beispielsweise in Form einer XML-Datei -, ist wohl ebenfalls mit der weltweiten Verwendung und der Beliebtheit zu erklären. Je mehr Anwender mit einem Produkt arbeiten, desto mehr Widerstand regt sich gegen die Einführung neuer Standards.
Eine Meal-Master-Datei besteht aus einem oder mehreren Rezepten, die untereinander in die Datei eingetragen sind. Jedes Rezept hat eine fest definierte erste und letzte Zeile. Die erste Zeile beginnt mit fünf großen M oder fünf Minuszeichen (-) und beinhaltet irgendwo dahinter die Zeichenfolge Meal-Master. Die letzte Zeile beginnt ebenso, ist aber nach diesen fünf Zeichen bereits zu Ende.
Dazwischen befinden sich drei Abschnitte: Im ersten, fest definierten Teil stehen Angaben zum Namen des Rezepts, zur Anzahl der Portionen für die angegebene Menge Zutaten und zu den Kategorien, denen das Rezept zugeordnet werden kann.
Im zweiten Teil befinden sich die Zutaten. Die Zutaten werden in ein oder zwei Spalten aufgelistet, wobei feste Bereiche der Zeile für die Informationen Menge, Einheit und Zutat vorgesehen sind.
Der dritte Teil beinhaltet schließlich die eigentliche Anleitung. Bild 2 zeigt ein Beispielrezept im Meal-Master-Format.
Diese Darstellung kann als einfachste Ausführung eines Rezeptes in diesem Format angesehen werden. Umfangreichere Rezepte besitzen weitere Zeilen, die z. B. die Zutaten nach den einzelnen Bestandteilen des Rezeptes aufteilen oder die einfach als Kommentarzeilen – beispielsweise zur Angabe der Herkunft eines Rezeptes – dienen. Solche Besonderheiten spart die im Folgenden erstellte Import-Routine aus Gründen der übersichtlichkeit allerdings aus.
Daten importieren
Function RezepttexteEinlesen(Quelldatei As String) Dim Zeile As String Dim Rezepttext As String Dim Rezeptnummer As Integer Dim Zeilennummer As Integer Dim db As Database Dim rstRezepttexte As Recordset On Error GoTo RezepttexteEinlesen_Err Set db = CurrentDb Set rstRezepttexte = _ db.OpenRecordset("tblRezepttexte", _ dbOpenDynaset) Open Quelldatei For Input As #1 Do While Not EOF(1) Line Input #1, Zeile If (Left(Zeile, 5) = "MMMMM" _ Or Left(Zeile, 5) = "-----") _ And InStr(1, Zeile, "Meal-Master") > 0 Then Zeilennummer = 1 Rezeptnummer = Nz(DMax("Rezeptnummer", _ "tblRezepttexte"), 0) + 1 End If rstRezepttexte.AddNew rstRezepttexte!Rezepttext = Zeile rstRezepttexte!Rezeptnummer = Rezeptnummer rstRezepttexte!Zeilennummer = Zeilennummer rstRezepttexte!EingelesenAm = Date rstRezepttexte.Update Zeilennummer = Zeilennummer + 1 Loop Close #1 Exit Sub RezepttexteEinlesen_Err: If Err.Number = 76 Then MsgBox "Die Datei konnte nicht gefunden " _ & "werden.", vbExclamation + vbOKOnly, _ "Dateifehler" Exit Function Else MsgBox "Fehler: " & Err.Number & " " _ & Err.Description End If End Function
Quellcode 1
Die übernahme von Daten aus Textdateien, die auf anderen Formaten als XML oder CSV basieren, bringt in der Regel Nacharbeit mit sich. Da die Dateien teilweise sehr große Mengen Rezepte enthalten – tausend und mehr sind keine Seltenheit -, soll die Datenbank den Inhalt der Datei zunächst einmal einlesen. Anschließend kann der Anwender dann bestimmen, welche Rezepte er in seine eigene Sammlung aufnehmen möchte.
Für die Zwischenspeicherung der Daten benötigen Sie eine Tabelle namens tblRezepttexte. Die Tabelle speichert die Daten so lange, bis der Anwender sie in seine Sammlung übernimmt.
Die Prozedur aus Quellcode 1 liest die Daten aus der Textdatei zeilenweise in die Tabelle tblRezepttexte ein.
Nach dem Deklarieren und Initialisieren der Objektvariablen öffnet die Prozedur mit der Open-Anweisung die Datei mit dem in dem Parameter Quelldatei angegebenen Namen.
Anschließend liest sie die Datei bis zum Erreichen des Dateiendes zeilenweise ein. Dabei berücksichtigt die Prozedur, ob ein neues Rezept beginnt, und setzt die Variable Rezeptnummer auf den Wert, der um 1 größer als der größte bisher vergebene Wert ist. Die Variable Zeilennummer wird mit jeder Zeile um 1 erhöht.
Beide Variablen werden mit dem Inhalt der Zeile und dem Datum des Einlesens in je einem Datensatz für jede Zeile in der Tabelle tblRezepttexte gespeichert.
Nach dem Einlesen ist kein weiterer Zugriff auf die Datei mehr erforderlich. Von nun an können alle benötigten Informationen zu den einzelnen Rezepten aus der Tabelle tblRezepttexte gelesen werden.
Bild 3: Formular zur übernahme von Rezepten
Die Funktion beinhaltet eine kleine Fehlerbehandlung, die bei Nichtvorhandensein der Datei mit dem übergebenen Namen eine entsprechende Meldung ausgibt.
Daten übernehmen
Der Import von Daten ist mit dem Einlesen der Daten in die Tabelle tblRezepttexte abgeschlossen, da nunmehr kein weiterer Zugriff auf die verwendete Datei mehr erforderlich ist. Im weiteren Verlauf sollen die Daten allerdings noch in das beschriebene Datenmodell übernommen werden. Zur komfortablen Auswahl der gewünschten Rezepte und zur Einleitung der anschließenden Konvertierung der vorhandenen Daten in das Datenmodell verwenden Sie ein geeignetes Formular.
Daten aus einer Textdatei in einer weiter verwertbaren Form in eine Datenbank zu bugsieren, ist keine besondere Kunst. Viel interessanter ist die übernahme der eingelesenen Daten in ein Datenmodell, das aufgrund seiner relationalen Struktur zwar wesentlich komplexer ist als der Ursprung der Daten, aber auch vielfältigere Möglichkeiten bietet.