Hallo liebe Accessfreunde,
ich möchte den Fehler "3022" umgehen und daher, bevor der Datensatz gespeichert werden kann, aber nachdem der Fehler auftritt über das Formular eine bestimmte ReferenzID zur verknüpften Tabelle in das betreffende Textfeld und damit in die Tabelle eintragen. Das habe ich mir so gedacht:
Private Sub Form_Error(DataErr As Integer, Response As Integer)
Dim rcsLaMaZu As DAO.Recordset
'Dim lngLaMaZID As Long
If DataErr = 3022 Then
Set rcsLaMaZu = CurrentDb.OpenRecordset("SELECT * FROM tblLadenMarkeZutat WHERE LaMaZladenIDRef = " & Me.cboLaden & " AND LaMaZmarkeIDRef = " & cboMarke & _
" AND LaMaZzutatIDRef = " & cboProdukt, dbOpenDynaset)
Me.txtZutatSammlLaMaZIDRef = rcsLaMaZu.Fields("LaMaZID").Value
' Response = acDataErrContinue
' MsgBox "Datensatz dupliziert " & DataErr & " " & Me.Name & " " & rcsLaMaZu.Fields("LaMaZID").Value
End If
End Sub
Die drei im Code benannten Felder habe ich mit einem Index versehen. Diese sind in einer Nachschlagetabelle referenziert. Den Referenzschlüssel zu diesem Datensatz mit diesen drei Feldern möchte ich in meinem Hauptformular anpassen, so dass die benannte Kombination aus diesen drei Feldern in der Nachschlagetabelle nicht kopiert wird. Den Referezschlüssel wollte ich über den Eintrag in Me.txtZutatSammlLaMaZIDRef ändern. Ich hatte erst gedacht, über die SQL-Funktion UPDATE den Datensatz zu aktualisieren, aber zu dem Zeitpunkt gibt es den Datensatz ja noch nicht richtig, weil dieser noch nicht gespeichert wurde. Also kann ich diesen wahrscheinlich auch noch nicht aktualisieren. Wenn ich jetzt einen Datensatz einfügen will und der Moment mit dem Fehler 3022 kommt, dann erscheint folgende Fehlermeldung:
Screenshot 2025-01-26 063209.png
Wenn ich die Windowshilfe bediene, erscheint folgende Erläuterung:
Zitat"Das aktuelle Feld muss mit dem Joinschlüsselnamen <> in der Tabelle übereinstimmen, die als 1-Seite der 1:n-Beziehung dient. (Fehler 3341)"
Es ist ja klar, wenn ich einen anderen Schlüssel einfüge, dass es dann erst mal nicht passt, aber wenn es gespeichert ist, müsste es doch gehen? Es ist ja mein Ziel, den Referenzschlüssel zu ändern.
Habt ihr da eine Idee, wie es gehen kann, dass ich dem Datensatz vor dem Speichern, die im Code benannte Referenz ID zuordnnen kann?
Hallo Konrad,
die Beziehungen kennst du ja und genau diese Bedingungen müssen erfüllt sein, damit ein DS überhaupt gespeichert werden kann. Was du da mit deinem Code im Form_Error Event beabsichtigst, ist mir nicht klar. Prüfungen und Validierungen gehören in das BeforeUpdate Event.
Gruß
Knobbi38
Hallo Knobbi38,
Zitat von: knobbi38 am Januar 26, 2025, 12:18:52die Beziehungen kennst du ja und genau diese Bedingungen müssen erfüllt sein, damit ein DS überhaupt gespeichert werden kann.
Ja, die kenne ich genau. Das bereitet mir ja das Kopfzerbrechen. Hier ein Bild vom Beziehungsfenster von der Abfrage, die meinem Formular hinterlegt ist:
Screenshot 2025-01-27 051720.png
und vom Formular:
Screenshot 2025-01-27 051935.png
Die Felder "LaMaZladenIDRef", "LaMaZmarkeIDRef", und "LaMaZmarkeIDRef" in der Tabelle "tblLadenMarkeZutat"sind indiziert (3 Felder Index). Wenn ich jetzt im Formular im Feld "cboLaden" für das Feld "LaMaZladenIDRef" einen Wert aus dem ausklappbaren Listenfeld auswähle, dann wird bereits in der Tabelle "tblLadenMarkeZutat" ein Wert für das Feld "LaMaZID" vergeben. Das kann ich auch mit dem "BeforUpdate"-Ereignis des Formulars NICHT unterbinden. Klar könnte ich das Formular anders, also mit ungebundenen Textfeldern im Endlosformular aufbauen und dann im "BeforeUpdate"-Ereignis noch mal eine Validierung machen, aber wie will ich dann ereichen, dass in jedem Datensatz die spezifischen Werte für die Felder "LaMaZladenIDRef", "LaMaZmarkeIDRef" und "LaMaZzutatIDRef" angezeigt werden?
Übrigens, sind die hier in der Abfrage aufgeführten Felder, die ich im Bild vom Formular nicht eingzeichnet habe, teilweise mit nicht sichbaren Steuerelementen verknüpft oder werden eben einfach nicht in diesem Form verwendet.
Hallo Konrad,
ZitatWenn ich jetzt im Formular im Feld "cboLaden" für das Feld "LaMaZladenIDRef" einen Wert aus dem ausklappbaren Listenfeld auswähle, dann wird bereits in der Tabelle "tblLadenMarkeZutat" ein Wert für das Feld "LaMaZID" vergeben.
Das wäre aber ungewöhnlich. Normalerweise werden Werte erst in einer Tabelle gespeichert, wenn der DS explizit gespeichert wird oder ein der DS ist Dirty und es wird zu einem neuen DS gewechselt.
Laut deinem Beziehungsbild finde ich kein Beziehung (Constraint) für das Feld "LaMaZladenIDRef", aber grundsätzlich kannst du so nur IDRefs in Tabelle tblLadenMarkeZutat speichern, wo es auch lt. Beziehung einen entsprechenden Eintrag in den Lookup-Tabellen dazu gibt; das sollte klar sein.
So ganz scheint dein Workflow noch nicht zu passen.
Gruß Knobbi38
Hallo,
man sollte auch darauf achten, dass bei allen Fremdschlüsselfeldern der Standardwert gelöscht wird. Bei Zahlenfeldern wird dieser automatisch auf 0 (die Zahl) gesetzt. Was dann zu Fehlern führt, wenn mal ein Fremdschlüsselfeld nicht belegt werden soll/kann, denn einen Fremdschlüssel 0 gibt es im Regelfall nicht.
Zitat von: knobbi38 am Januar 27, 2025, 12:35:43Das wäre aber ungewöhnlich. Normalerweise werden Werte erst in einer Tabelle gespeichert, wenn der DS explizit gespeichert wird oder ein der DS ist Dirty und es wird zu einem neuen DS gewechselt.
Bei mir scheint es so zu sein, dass der eine Datensatz in der einen Tabelle gespeichert wird und dann ein Datensatz in der verknüpften Tabelle erzeugt wird und wenn dieser dann alle Informationen hat, im Datensatz der vorhergehenden Tabelle weiter geschrieben wird.
Zitat von: knobbi38 am Januar 27, 2025, 12:35:43Laut deinem Beziehungsbild finde ich kein Beziehung (Constraint) für das Feld "LaMaZladenIDRef", aber grundsätzlich kannst du so nur IDRefs in Tabelle tblLadenMarkeZutat speichern, wo es auch lt. Beziehung einen entsprechenden Eintrag in den Lookup-Tabellen dazu gibt; das sollte klar sein.
Stimmt, die Nachschlagetabelle für das Feld "LaMaZladenIDRef" ist in dieser Abfrage nicht enthalten.
Zitat von: MzKlMu am Januar 27, 2025, 12:40:30man sollte auch darauf achten, dass bei allen Fremdschlüsselfeldern der Standardwert gelöscht wird.
Danke für diesen Hinweis. Mache ich das im Eigenschaftsfenster für das Feld unter "Standardwert" und hier dann eine 0 eintragen?
Hallo,
Zitatfür das Feld unter "Standardwert" und hier dann eine 0 eintragen?
Sorry, aber wo habe ich geschrieben, dass Du eine 0 eintragen sollst ?
Du sollst die 0 als Standardwert
entfernen, die im
Tabellenentwurf in Zahlenfelder automatisch eingetragen wird. Bei Fremdschlüsselfeldern ist das wichtig.
Hallo Konrad,
wenn bei dir automatisch etwas gespeichert wird, wird es dafür einen Grund geben, der aber so nicht nachvollziehbar ist. Ein Beispiel-DB, wo das Verhalten nachvollzogen werden kann, wäre da schon hilfreich.
Gruß Knobbi38
Hallo,
man sollte sich angewöhnen, darauf zu achten, daß UPDATE und INSERT immer nur mit genau einer Tabelle funktionieren.
Man kann Verknüpfungen in Abfragen einfügen und die Felder, die sich daraus ergeben, kann man für Prüfungen wie in WHERE oder JOIN verwenden - aber geändert wird immer nur EINE Tabelle. Das ist auch bei den "Großen" so.
Access nimmt hier allerdings eine Sonderrolle ein, weil Access versucht, über eigene Automatismen diese Regel zu umgehen. Damit kann man oft Daten in mehr als eine Tabelle einfügen mit einer verknüpften Tabelle. Access versucht dann, die PK-ID in einer Lookup-Tabelle anhand der Eingabedaten selbst zu erzeugen und gleich auch als FK-ID in die untergeordnete Tabelle einzufügen - sofern man das PK-ID-Feld mit in der Abfrage hatte.
In SQL Server geht das nur zum Teil, da kann man auch eine Abfrage mit JOINs zusammenstellen und Daten von z.B. 2 Tabellen gleichzeitig bearbeiten - allerdings immer nur mit einer Tabelle davon. Also entweder Daten der einen Tabelle oder Daten der anderen Tabelle ändern, das geht dann in z.B. SSMS auch.
Das ist aber alles sehr unschön, denn es kann eben dazu führen, daß man eine PK-ID erstellt, wo ein Datensatz schon existiert und ohne passende Indizierung hat man dann "ähnliche" Datensätze in einer Lookup-Tabelle stehen. Und wird die PK-ID nicht in der Abfrage eingeschlossen, kann Access sie auch nicht erstellen und dann meckert Access, weil es den passenden PK-ID-Eintrag nicht in der Lookup-Tabelle finden kann (zu Recht).
Meine Empfehlung: Ein Formular darf immer nur eine Tabelle verändern.
Wenn es um Rezepte geht, würde ich persönlich Rezepte und Zutaten als direkten Zusammenhang sehen. Verbunden über eine m:n-Tabelle, die je RezeptID die jeweilige Zutat, ihre Menge und die Einheit aus einer Einheiten-Lookup-Tabelle zusammenführt. Völlig unwichtig beim Rezept ist der Laden und die Marke (erst einmal).
In zweiter Instanz würde ich die Produkte als Produktstammdaten erstellen. Ein Produkt beschreibt das Produkt selbst, also etwa Butter. Dann gäbe es eine Tabelle Marken, die beschreibt generell, welche Marken es gibt, ohne Produkte, ohne Läden. Die Produkte können dann mit m:n zu Marken verbunden werden. Das gleiche Produkt gibt es bei verschiedenen Marken und verschiedene Marken können mehrere Produkte haben. Dann gäbe es eine weitere Tabelle für Verpackungsgrößen mit Menge und Einheit. Diese kann dann per m:n mit der ProdukteZuMarkeID und der VerpackungsID zusammengeführt werden, so daß es ein Produkt bei einer Marke (ProdukteZuMarkeID) mit verschiedenen Verpackungsgrößen geben kann.
Als letzte Instanz kämen dann die Anbieter, also Läden, hinzu. Aus der m:n-Tabelle käme ProdukteZuMarkeZuVerpackungID und beschreibt damit konkret ein Produkt einer Marke und deren Verpackungsgröße, und die neue m:n-Tabelle verbindet diese ID dann mit der LadenID. Nun kann man genau sagen, welcher Laden von welcher Marke welches Produkt in welcher Verpackungsgröße anbietet.
Diese neue ID dieser m:n-Tabelle kann man wiederum in der Zutaten zu Produkte-m:n-Tabelle als ZusatzID aufnehmen, um z.B. zu ermöglichen, bei einem Rezept ganz bestimmte Hersteller und Läden zu berücksichtigen. Vermutlich aber sinnvoller, zwei Stufen darüber zu gehen und nur bis zur Marke zu Produkt zu gehen, denn wo man ein Produkt letztlich kauft, spielt für ein Rezept normalerweise keine Rolle.
Ein entsprechendes Formular sähe für mich so aus, daß ein HFo das Rezept beschreibt und wie man es herstellt (der Freitext) usw., ein UFo mit den Zutaten und notwendigen Mengen, ein weiteres UFo, das zu jeder ausgewählten Zutat die möglichen Verpackungsgrößen anzeigt und bei Auswahl einer Verpackungsgröße die Bezugsquellen.
Je nachdem, was man wirklich am Ende mit in das Rezept speichern möchte, kann man die ausgewählten IDs in passende Felder in übergeordneten Tabellen speichern. Also etwa die Verpackungsgröße als empfohlene Größe in die Zutatenliste. Verpackungsgrößen können sich ändern, aber dann will man nicht unbedingt das Rezept oder seine Zutaten oder die benötigten Mengen ändern. Wenn man 500g Rama braucht und Rama auf einmal nur noch 400g-Becher anbietet, dann helfen die Informationen nicht, daß Rewe mal 500g angeboten hat. Ein Einkauf würde jetzt 2 Becher benötigen und nicht wie vorher nur einer. Vielleicht nimmt der Laden das Produkt oder eine Verpackungsgröße raus - usw. Entsprechend muß man die wichtigen Informationen wie Rezeptname und Beschreibung in übergeordnete Tabellen schreiben und Informationen, die sich ständig verändern können, in untergeordnete Tabellen.
Durch so eine Strukturierung kann man z.B. programmiertechnisch reagieren, wenn Rama nur noch 400g-Becher anbietet und 500g benötigt werden in einem Rezept. Dann kann man die Rezepte ausfiltern, die dann zu wenig Menge im gewählten Produkt hätten und diese entsprechend automatisiert oder mit Benutzerinteraktion anpassen.
Die Strukturierung hilft dann auch, das Prinzip 1 Formular = 1 Tabelle beizubehalten. Daten aus Lookup-Tabellen führt man dann über z.B. Komboboxen zusammen, die nur die ID liefern, aber die übrigen Informationen anzeigen, ohne daß man sie editieren kann. Deren Spalten kann man dann wiederum auch in getrennten Textboxen anzeigen. Somit sieht das Formular nach viel mehr Informationen aus, während in Wirklichkeit nur genau 1 Tabelle bearbeitet wird.
Wenn man sich strikt an das Prinzip hält, gibt es auch keine Fehler in Richtung fehlender PK-ID etc.
Gruß
Christian
Zitat von: MzKlMu am Januar 29, 2025, 08:41:01Sorry, aber wo habe ich geschrieben, dass Du eine 0 eintragen sollst ?
Das hast du nicht geschrieben. Das hatte ich nur vermutet.
Zitat von: MzKlMu am Januar 29, 2025, 08:41:01Du sollst die 0 als Standardwert entfernen, die im Tabellenentwurf in Zahlenfelder automatisch eingetragen wird. Bei Fremdschlüsselfeldern ist das wichtig.
Danke. Setze ich um.
Hallo Christian,
Zitat von: Bitsqueezer am Januar 29, 2025, 12:14:41man sollte sich angewöhnen, darauf zu achten, daß UPDATE und INSERT immer nur mit genau einer Tabelle funktionieren.
Man kann Verknüpfungen in Abfragen einfügen und die Felder, die sich daraus ergeben, kann man für Prüfungen wie in WHERE oder JOIN verwenden - aber geändert wird immer nur EINE Tabelle.
Danke. Da habe ich wieder was gelernt.
Zitat von: Bitsqueezer am Januar 29, 2025, 12:14:41Access versucht dann, die PK-ID in einer Lookup-Tabelle anhand der Eingabedaten selbst zu erzeugen und gleich auch als FK-ID in die untergeordnete Tabelle einzufügen - sofern man das PK-ID-Feld mit in der Abfrage hatte.
Das ist mir auch schon aufgefallen.
Zitat von: Bitsqueezer am Januar 29, 2025, 12:14:41In SQL Server geht das nur zum Teil, da kann man auch eine Abfrage mit JOINs zusammenstellen und Daten von z.B. 2 Tabellen gleichzeitig bearbeiten - allerdings immer nur mit einer Tabelle davon.
Aha, Danke. Also tendenziell eine Tabelle, auch für einen einfachen Umstieg auf SQL-Server, für evtl. Webanwendungen.
Zitat von: Bitsqueezer am Januar 29, 2025, 12:14:41Das ist aber alles sehr unschön, denn es kann eben dazu führen, daß man eine PK-ID erstellt, wo ein Datensatz schon existiert und ohne passende Indizierung hat man dann "ähnliche" Datensätze in einer Lookup-Tabelle stehen. Und wird die PK-ID nicht in der Abfrage eingeschlossen, kann Access sie auch nicht erstellen und dann meckert Access, weil es den passenden PK-ID-Eintrag nicht in der Lookup-Tabelle finden kann
Ich habe die betreffenden 3 Felder der Abfrage ja indiziert. Aufgrund dessen ist eine Duplzierung der Datensätze an der Stelle nicht möglich. Den PK der verknüpften Tabelle von tblLadenMarkeZutat habe ich nicht übernommen, aber einen neuen Datensatz kann ich in dieser Tabelle trotzdem erzeugen, wenn er noch nicht vorhanden ist und ich die entsprechende Datenkombinaton über das Formular eingebe. Über den Fremdschlüssel "ZutatSammlLaMaZIDRef", scheint das möglich.
Zitat von: Bitsqueezer am Januar 29, 2025, 12:14:41In zweiter Instanz würde ich die Produkte als Produktstammdaten erstellen.
Du machst also eine Unterscheidung zwischen Produkt und Zutat. Was ist da genau der Untschied?
Zitat von: Bitsqueezer am Januar 29, 2025, 12:14:41Dann gäbe es eine weitere Tabelle für Verpackungsgrößen mit Menge und Einheit. Diese kann dann per m:n mit der ProdukteZuMarkeID und der VerpackungsID zusammengeführt werden, so daß es ein Produkt bei einer Marke (ProdukteZuMarkeID) mit verschiedenen Verpackungsgrößen geben kann.
Verstehe ich das richtig, dass du vorschlägst eine m:n Tabelle zu erstellen, die Produkte, Marken, Verpackungseinheiten, Mengen und Einheiten vereint?
Zitat von: Bitsqueezer am Januar 29, 2025, 12:14:41Als letzte Instanz kämen dann die Anbieter, also Läden, hinzu. Aus der m:n-Tabelle käme ProdukteZuMarkeZuVerpackungID und beschreibt damit konkret ein Produkt einer Marke und deren Verpackungsgröße, und die neue m:n-Tabelle verbindet diese ID dann mit der LadenID. Nun kann man genau sagen, welcher Laden von welcher Marke welches Produkt in welcher Verpackungsgröße anbietet.
Diese neue ID dieser m:n-Tabelle kann man wiederum in der Zutaten zu Produkte-m:n-Tabelle als ZusatzID aufnehmen, um z.B. zu ermöglichen, bei einem Rezept ganz bestimmte Hersteller und Läden zu berücksichtigen. Vermutlich aber sinnvoller, zwei Stufen darüber zu gehen und nur bis zur Marke zu Produkt zu gehen, denn wo man ein Produkt letztlich kauft, spielt für ein Rezept normalerweise keine Rolle.
Verstanden.
Zitat von: Bitsqueezer am Januar 29, 2025, 12:14:41Ein entsprechendes Formular sähe für mich so aus, daß ein HFo das Rezept beschreibt und wie man es herstellt (der Freitext) usw., ein UFo mit den Zutaten und notwendigen Mengen, ein weiteres UFo, das zu jeder ausgewählten Zutat die möglichen Verpackungsgrößen anzeigt und bei Auswahl einer Verpackungsgröße die Bezugsquellen.
Wenn alles in einer Tabelle ist, kann leichter auf die Daten zugegriffen werden. Macht für mich absolut Sinn.
Zitat von: Bitsqueezer am Januar 29, 2025, 12:14:41Die Strukturierung hilft dann auch, das Prinzip 1 Formular = 1 Tabelle beizubehalten. Daten aus Lookup-Tabellen führt man dann über z.B. Komboboxen zusammen, die nur die ID liefern, aber die übrigen Informationen anzeigen, ohne daß man sie editieren kann. Deren Spalten kann man dann wiederum auch in getrennten Textboxen anzeigen. Somit sieht das Formular nach viel mehr Informationen aus, während in Wirklichkeit nur genau 1 Tabelle bearbeitet wird.
Wenn man sich strikt an das Prinzip hält, gibt es auch keine Fehler in Richtung fehlender PK-ID etc.
Da schlage ich dann viele Fliegen mit einer Klappe. Das mit den Informationen anzeigen aus Lookup-Tabellen mithilfe von Komboboxen und Textboxen habe ich ja schon umgesetzt
Hallo Konrad,
ZitatDu machst also eine Unterscheidung zwischen Produkt und Zutat. Was ist da genau der Untschied?
Naja, eine Zutat ist "Butter". Ein Produkt ist "Kerrygold Butter" oder "Landliebe Butter".
ZitatVerstehe ich das richtig, dass du vorschlägst eine m:n Tabelle zu erstellen, die Produkte, Marken, Verpackungseinheiten, Mengen und Einheiten vereint?
m:n-Tabellen müssen nicht immer nur 2 Endtabellen verbinden. Da jede m:n-Tabelle immer eine eigene AutoID haben sollten, kann man auch diese verwenden, um sie in weiteren m:n-Tabellen zu verwenden. So kann man eine Kombination einer m:n-Tabelle mit entweder einer Endtabelle oder einer anderen m:n-Tabelle verbinden. Dadurch müssen Kombinationen nicht ständig wiederholt werden, sondern man definiert eine Kombination einmal und kann sie dann wiederverwenden. Also Produkte zu Marken ist eine m:n-Tabelle, die zwei Endtabellen verbindet. Dadurch hast Du eine Kombination eines Produktes mit einer Marke. Also "Landliebe Butter". Und wenn Du auf deren AutoID nun eine weitere m:n-Tabelle zu Verpackungsgrößen bildest, kannst Du eine m:n zu einer weiteren m:n zu einer Endtabelle verbinden. So kannst Du "Landliebe Butter" in einer Zeile einer m:n definieren und erhältst daraus eine ID. Und in der nächsten m:n verwendest Du diese m:n-Produkt/Marke-ID zusammen mit der Endtabelle Verpackungsgrößen. Und schon hast Du "Landliebe Butter 100g" und "Landliebe Butter 500g". Dennoch sind Marken, Produkte, Verpackungen immer noch getrennte Tabellen und so kann man am Ende beliebige Abfragen daraus erstellen, etwa: Welche Marken haben Butter im Programm? Welche Verpackungsgröße mit 500g gibt es für welche Produkte? Gibt es 100g-Packungen für Butter bei irgendeiner Marke? Usw.
Aber eben nur bei Auswertungen, nicht bei der Eingabe. Als Benutzer möchte ich nicht für jede neue Verpackungsgröße erst "Butter", dann "Landliebe", dann Verpackungsgröße eingeben, und das jedesmal. Ich gehe auf die Zutat "Butter" stattdessen und definiere dann, welche Marken es gibt, und wenn ich auf der Zeile "Landliebe" stehe, definiere ich in einem weitere UFo, welche Verpackungsgrößen es für diese Marke und dieses Produkt gibt. So kann ich als Benutzer ganz schnell 10 Verpackungsgrößen eingeben, ohne die redundanten Informationen jedesmal erneut auswählen zu müssen. Das macht die UFo-Verkettung automatisch für mich.
Und das kann gilt dann auch für Läden natürlich usw. Das Beispiel war jetzt nur für diese drei Informationen.
ZitatWenn alles in einer Tabelle ist, kann leichter auf die Daten zugegriffen werden. Macht für mich absolut Sinn.
Bin mir nicht sicher, ob Du das richtig verstanden hast. Es geht nicht darum, alle Informationen in EINER Tabelle zu halten, sondern ganz im Gegenteil aufzutrennen in Informationen, die zusammengehören, in getrennten Tabellen. Das verhindert zum einen das doppelte Erfassen der gleichen Information, was man mit einem Unique Index in m:n-Tabellen und mit Unique Index auf Namen wie Produktnamen oder Markennamen verhindern kann, zum anderen das Schema "ein Formular = 1 Tabelle" durch Auftrennung in getrennte Unterformulare erreichen kann, die zudem für den Benutzer maximalen Komfort bieten, weil er bedeutend weniger eingeben muß und alle Informationen säuberlich getrennt gelistet bekommt.
Gruß
Christian
Hallo Christian,
Zitat von: Bitsqueezer am Januar 30, 2025, 13:08:52Naja, eine Zutat ist "Butter". Ein Produkt ist "Kerrygold Butter" oder "Landliebe Butter".
Danke noch mal für die Klarstellung. Das war mir vorher nicht bewusst.
Zitat von: Bitsqueezer am Januar 30, 2025, 13:08:52So kannst Du "Landliebe Butter" in einer Zeile einer m:n definieren und erhältst daraus eine ID. Und in der nächsten m:n verwendest Du diese m:n-Produkt/Marke-ID zusammen mit der Endtabelle Verpackungsgrößen. Und schon hast Du "Landliebe Butter 100g" und "Landliebe Butter 500g". Dennoch sind Marken, Produkte, Verpackungen immer noch getrennte Tabellen und so kann man am Ende beliebige Abfragen daraus erstellen, etwa: Welche Marken haben Butter im Programm? Welche Verpackungsgröße mit 500g gibt es für welche Produkte? Gibt es 100g-Packungen für Butter bei irgendeiner Marke? Usw.
Langsam schimmert mir, dass dann die Dateneingabe leichter wird, weil die Datensatzquelle des Formulars auf einer Tabelle beruht. Dann habe ich es mir in der Vergangenheit echt viiiiiiiel zu schwer gemacht. Danke für den Tip. Das hat mich echt nach vorne gebracht.
Zitat von: Bitsqueezer am Januar 30, 2025, 13:08:52Aber eben nur bei Auswertungen, nicht bei der Eingabe. Als Benutzer möchte ich nicht für jede neue Verpackungsgröße erst "Butter", dann "Landliebe", dann Verpackungsgröße eingeben, und das jedesmal.
Genau, das hat mich bei der Dateneingabe so genervt. Dann baue ich das Formular noch mal neu.
Zitat von: Bitsqueezer am Januar 30, 2025, 13:08:52Ich gehe auf die Zutat "Butter" stattdessen und definiere dann, welche Marken es gibt, und wenn ich auf der Zeile "Landliebe" stehe, definiere ich in einem weitere UFo, welche Verpackungsgrößen es für diese Marke und dieses Produkt gibt. So kann ich als Benutzer ganz schnell 10 Verpackungsgrößen eingeben, ohne die redundanten Informationen jedesmal erneut auswählen zu müssen. Das macht die UFo-Verkettung automatisch für mich.
Also anstatt die gleichen Informationen immer wieder einzugeben, lieber ein Unterformular erstellen, in dem ich dann die spezifischen Daten erfasse.
Ich habe hier mal ein Bildschirmfoto meiner neu gestalteten Datenbank gemacht, so wie ich es aus deinen Ausführungen verstanden habe:
Screenshot 2025-01-31 060804.png
Kannst du mir noch schreiben, ob das mit den Tabellen "tblAblauf" und "tblArbeitsschritte" so hilfreich für die Formulargestaltung ist? Außerdem bin ich mir nicht so sicher, ob die Angabe der Nährwerte für die Zutaten in der Tabelle "tblProdukt" sinnvoll ist, oder ob ich da noch eine Nachschlagetabelle pro Produkt, nur für die Nährwertangaben machen sollte. Die Tabelle für die Einheiten habe ich in einer Tabelle zusammengefasst, da ich hier nur 1x eine Einheit erfassen muss. Macht das Sinn?
Hallo Konrad,
ein Anfang ist gemacht, ginge mir persönlich allerdings normalisierungstechnisch noch nicht weit genug.
tblZutaten: Die Nährwerte passen hier gar nicht. Jedes Produkt hat seine eigene Nährwerttabelle, ein Pfund Butter von Produkt A hat längst nicht die gleichen Nährwerte wie Produkt B. Entsprechend gehört in Zutaten zur Hauptsache der Name hinein und die Einheit. Also "Butter", "g" (oder auch die KategorieID). Auch keine weitere z.B. rezeptspezifische Information wie "besonders streichfähig". Die Zutat ist Butter. Details, die ein Rezept betreffen, gehören zu Rezeptdetails. Die Zutat beschreibt nur ganz basismäßig die Zutat.
Nährwerte: Weder in tblProdukt noch in tblZutat wäre es in Deinem Beispiel richtig gelöst, von der Normalisierung abgesehen. Denn diese Art der Gestaltung nennt sich "Aufzählungsfelder". Felder, die inhaltlich gleiche Art von Aussagen treffen, gehören in eine Tabelle. Das ist das gleiche wie Felder mit "Januar", "Februar"... oder "Jahr2024", "Jahr2025" usw. Aufzählungsfelder sind immer ein Zeichen falscher Normalisierung.
Richtig wäre es, mal von tblProdukte ausgehend, eine Lookup-Tabelle für NährwertArt zu haben, also Kilokalorien, Joule, was auch immer. Eine weitere Tabelle ProduktZuNährwert beschreibt die Liste der Nährwerte dieses Produktes. Da diese heute zum Glück i.d.R. normiert auf 100g-Einheiten sind, hat man dann gute Vergleichswerte und muß selbst nichts umrechnen. Also ProduktID, NährwertArtID, Nährwert (die Zahl). Nun kannst Du zu jedem Produkt beliebig viele Nährwertarten erfassen und eingeben, was nicht vorliegt, wird auch nicht eingegeben. 10 Zeilen bei einem Produkt, 3 bei einem anderen. Eine Standard-Einheit wird in den Stammdaten der NährwertArt als EinheitID hinterlegt, so daß der Benutzer hier gar nichts auswählen muß - er muß nur die Nährwertart auswählen, bekommt die Einheit automatisch angezeigt und muß dann nur den Wert eingeben.
In die Produkttabelle gehören auch keine Gewichte. Das wird ja in der Zuordnung der Verpackungseinheit geregelt. Das Produkt ist "Rama", der Hersteller ist "Upfield Holdings". Sorry, wenn ich "Marke" oben gesagt habe, es muß natürlich "Hersteller" sein. Das Produkt ist ja bereits der Markenname.
Entsprechend kann man hier auch einfach eine 1:n-Beziehung bauen, da ein Produkt nur von einem Hersteller kommen kann (i.d.R.), wenn man es flexibel halten will und mehrere Hersteller an einem Produkt beteiligt sind und man es ganz akribisch haben will, kann man aber auch eine m:n-Tabelle machen.
Eine m:n zur Verpackungseinheit wird dann aber notwendig - ein Produkt eines Herstellers zur gewünschten Verpackungseinheit. In dieser m:n wird dann die Verpackungseinheit ("Eimer", "Becher", "Glas", "Tüte") als ID hinterlegt (neben der ProduktID). Diese m:n (ProdukteZuVerpackungseinheiten) ist die, die ein Produkt endgültig beschreibt.
Die Nährwerte werden dann entweder an das Produkt oder an diese m:n angehängt (also 1:n an deren AutoID). Also definiert man entweder, daß ein bestimmtes Produkt wie Rama IMMER diese Nährwerte hat oder unterscheidet je Verpackungsgröße dieses Produktes, wenn es da Abweichungen geben kann. Für eine Rezepttabelle würde ich es an die ProduktID anhängen, da kommt es nicht auf zu kleine Details an und der Eingabeaufwand ist dann extrem viel geringer.
Für eine Datenbank für Ernährungswissenschaftler ist es vielleicht im Detail wichtiger.
Bei der RezepteZuZutaten-m:n würde ich keine Einheit verlinken. Eine Zutat definiert den Namen und die Einheit. Die Menge gibt in der m:n dann an, wieviel von dieser Einheit verwendet werden muß.
Hier würde für mich noch ein Beschreibungsfeld hineingehören, das Spezialitäten der Zutat für speziell dieses Rezept und diese Zutat als Freitext beschreibt, etwa wie "vor Verwendung auf Zimmertemperatur bringen" oder sowas. Das ist ja kein Arbeitsschritt, nur eine Beschreibung, in welchem Zustand die Zutat für dieses Rezept sein sollte.
Arbeitsschritte getrennt zu halten, finde ich eine gute Idee. Die Arbeitsschritte können nicht 1:1 auf die Rezeptzutaten gemappt werden, sie können auch Schrittbeschreibungen enthalten, die keine Zutat erfordern, etwa "und jetzt...mengen wie 'ne Wahnsinnige!"... :)
Zeit getrennt zu erfassen - naja, kann man machen, wenn man errechnen will, wie lange die Herstellung benötigt. Das kann man selbst entscheiden, ob einem das wichtig ist.
Arbeitsschritte würde ich allerdings 1:n definieren. Ich denke, es gibt nur wenige Rezepte, die die gleichen Arbeitsschritte wiederverwenden. Man könnte sich hier überlegen, eine "Makrotabelle" zu erzeugen, um Basistexte zu hinterlegen, von denen man einen auswählen und in die Arbeitsschritte übernehmen zu können, dort aber noch anpassen zu können. Die Schritte dürften immer sehr individuell sein, aber so eine "Textbaustein-Tabelle" kann helfen, lange Eingaben zu ersparen. Eine Referenz braucht es dann nicht, da dann der Text aus der Bausteintabelle in die eigentliche Arbeitsschritt-Tabelle übernommen werden würde, um sie dort anzupassen. Also keine ID.
Die Tabelle Produkte würde ich hier vor allem an die ZutatID binden (also mit hinein). Denn ein Produkt "Motoröl" wird sicher nie an eine Zutat "Butter" gebunden werden.
Denn wenn ich die Zutat "Butter" auswähle, habe ich dann sofort eine Liste aller Produkte, Hersteller und Verpackungseinheiten. Damit kann man in genau einer Kombobox bei den Rezeptzutaten die Zutat, den Hersteller, das Produkt und die Verpackungseinheit auswählen und benötigt nur die ID der m:n-Tabelle - da diese ja schon die ZutatID dann beinhaltet, ist damit alles drin, was man braucht. Das Feld "ZutatID" entfällt dann in der Rezept-Zutat-Tabelle.
Bei der Eingabe vereinfacht es sich dann auch: Wenn man Zutaten verwalten will, hat man ein Endlosformular mit der Zutatentabelle (1 Formular = 1 Tabelle), darin ein Unterformular, auf die Produkte-Tabelle als Endlosformular (wieder 1 Formular = 1 Tabelle), hier kann man dann auch gleich neue Produkte erfassen, die dann automatisch zu genau dieser Zutat gehören. Darunter dann die m:n-Tabelle, die die Zuordnung zwischen Hersteller, Produkt und Verpackungseinheit bildet (wie gesagt, Hersteller würde ich eher an die Produkte binden). Wieder: 1 Formular = 1 Tabelle. Da die IDs hier die ProduktID "von oben" vererbt bekommen, ist für jedes ausgewählte Produkt die Liste der Verpackungseinheiten zu sehen, die man hier erweitern/verändern kann (dringende Empfehlung: Keine Löschweitergabe verwenden, die übergeordneten Datensätze sollen zum Löschen gesperrt sein, wenn sie Daten der untergeordneten bereits verwenden).
Damit kann man Zutaten->Produkte->Verpackungen in einem Formular als Stammdaten komfortabel bearbeiten. Rezeptunabhängig.
Da die EinheitsID bereits in der Zutat hinterlegt ist, muß man sie nicht mehr in der Produkte-Tabelle oder sonstwo definieren. Butter = g, egal von welchem Hersteller, egal in welchem Rezept. Eine Zeile definiert für 100 Butterarten, fertig.
Ich empfehle auch, die Namensgebung zu überdenken. Du mußt nicht in jeder Tabelle für jedes Feld den Namen der Tabelle wiederholen. In jeder ordentlichen SQL-Abfrage hast Du entweder den vollständigen Tabellennamen oder einen Tabellenalias, der Dir aufzeigt, welches Feld aus welcher Tabelle kommt. Dann ist es enorm lang, "tblRezepte.RezepGewicht" zu schreiben, obwohl (bei Alias "Rzp") auch ausreichen würde: "Rzp.Gewicht". Ganz abgesehen davon, daß diese kryptischen Abkürzungsorgien auch echt schwer zu lesen und zu verstehen sind, wenn man nicht die ganze Datenbank kennt. "RezuehMaPrVIDIDRef" (2mal ID? Und was ist Ref hier?) - grausig... :)
Viel wichtiger, Namen lesen und verstehen zu können. Die können i.d.R. bis 128 Zeichen lang sein, die Zeiten von 8stelligen Namen sind schon lange vorbei. Selbstbeschreibende Felder sind viel wichtiger.
Auch bei "Ref" oder "_F", wie es Klaus meistens macht - meine Meinung nach wie vor: Man macht sich das Leben nur schwerer. Einfach den gleichen ID-Namen verwenden und "ID" immer voranstellen, dann sieht man sie sofort, sie werden in alphabetischen Listen sofort untereinander angezeigt und mit dem selsbtverständlichen Alias weiß man immer genau, was wohin kommt. Nur nicht einfach "ID", HIER sollte der Tabellenname immer rein, also "ID_Rezept" etwa.
Auch diese Beschreinbung hier ist vermutlich noch nicht ideal, war erst mal nur so rein gedanklich, in der Praxis stellt man sicher hier und da noch Änderungen fest. Allerdings, bevor man das erste Formular macht, alles erst mal auf Tabellenebene testen, am besten durch direkte Eingaben, dann sieht man schnell, wo man was umstellen muß.
Gruß
Christian
Hallo,
zu den Feldnamen zitiere ich mich aus einem Thema des TE vom Februar 2024:
Zitat von: MzKlMu am März 04, 2024, 00:22:59Die Feldnamen die Du da gemacht hast sind gruselig. Du als Entwickler weist das natürlich. Aber eine Fremder tut sich da schwer mit. Und wenn Du 4 Wochen an der DB nichts gemacht hast, weist Du das auch nicht mehr.
ZitatAuch bei "Ref" oder "_F", wie es Klaus meistens macht - meine Meinung nach wie vor: Man macht sich das Leben nur schwerer.
Es geht mir dabei ja nur um die Anfänger, wie ich bereits mehrfach schon schrieb.
Hier im Forum gab es erst vor kurzem ein Thema bei dem PS und FS verwechselt wurden. Bei Verknüpfen von/nach PS/FS verwechselt und bei Kombis zur Auswahl auch.
Erst das Anfügen eines _F hat die Sache sehr schnell gekärt.
Muss mal schauen, ob ich das Thema noch finde.
Ich möchte keine erneute Diskussion darüber, sondern nur noch mal meine Gründe schildern.
Hallo Klaus,
alles gut. Jeder hat so seine Meinungen und Methoden. Ich habe nie gesagt, daß Deine falsch oder völlig daneben ist... :) Meine ist auch nichts für Jeden.
Gruß
Christian
Hallo Christian,
danke für deine sehr ausführliche Antwort.
Zitat von: Bitsqueezer am Januar 31, 2025, 13:57:01Die Nährwerte passen hier gar nicht.
Danke. Das nächste mal prüfe ich besser
. Ich würde in der Tabelle "tblZutaten" auch die Gewichte weglassen, denn die sind ja schon in der Tabelle "tblProdukte" enthalten.
Zitat von: Bitsqueezer am Januar 31, 2025, 13:57:01In die Produkttabelle gehören auch keine Gewichte.
Hier soll es darum gehen, dass ein Esslöffel Rama z.B. 10 Gramm und ein Esslöffel Erdnussbutter von Marke XYZ 8 Gramm wiegt. Das ist wichtig, wenn ich ein Rezept als Zutat für ein anderes Rezept haben und die Nährwerte berechnen will. Dann brauche ich immer das Zugabegewicht vom Produkt und dessen Nährwert pro 100 Gramm, damit ich die tatsächlich zugeführte Nährwertmenge berechnen kann.
Zitat von: Bitsqueezer am Januar 31, 2025, 13:57:01Nun kannst Du zu jedem Produkt beliebig viele Nährwertarten erfassen und eingeben, was nicht vorliegt, wird auch nicht eingegeben.
In meiner bisherigen Datenbank, gab es viel Probleme mit Feldern, die noch NULL sind. Das fällt damit weg. Sehr hilfreich.
Zitat von: Bitsqueezer am Januar 31, 2025, 13:57:01Eine Standard-Einheit wird in den Stammdaten der NährwertArt als EinheitID hinterlegt
Mir ist nicht ganz klar, was du mit Stammdaten meinst. Meinst du die Felder oder meinst du die Eigenschaft "Standardwert" in den Eigenschaften eines Feldes oder meinst du etwas Anderes?
Zitat von: Bitsqueezer am Januar 31, 2025, 13:57:01In die Produkttabelle gehören auch keine Gewichte. Das wird ja in der Zuordnung der Verpackungseinheit geregelt.
Das kann sein, aber wie sonst soll ich die Gewichte für die einzelnen Rezepteinheiten den Produkten zuordnen? Wenn ich die Nährwertmenge für das gesamte Rezept bestimmen will, dann muss ich wissen, wieviel z.B. ein EL von Rama oder Erdnussbutter wiegt, um ihn dann mit der durch 100 geteilten Nährwertangabe für den entsprechendn Nährwert multiplizieren zu können. Das mit allen Zutaten für das Rezept für alle Nährwerte, ergibt die gesamte Nährwertmenge für das Rezept.
Zitat von: Bitsqueezer am Januar 31, 2025, 13:57:01Für eine Rezepttabelle würde ich es an die ProduktID anhängen, da kommt es nicht auf zu kleine Details an und der Eingabeaufwand ist dann extrem viel geringer.
Das würde ich auch so machen. Das reicht mir vollkommen aus.
Zitat von: Bitsqueezer am Januar 31, 2025, 13:57:01Bei der RezepteZuZutaten-m:n würde ich keine Einheit verlinken.
Alles klar. Aber wenn ich ein Rezept als Zutat verwenden will. Z.B. Mehl aus Leinsamen, Sonnenblumenkernen und Mandeln, dann muss ich wissen, was ein EL davon wiegt und dafür benötige ich das Gewicht für einen EL für dieses Rezept
Zitat von: Bitsqueezer am Januar 31, 2025, 13:57:01Zeit getrennt zu erfassen - naja, kann man machen, wenn man errechnen will, wie lange die Herstellung benötigt.
Ja, absolut. Um zu filtern, welches Rezept unter 30 oder 15 oder irgendeiner anderen Zeitvorgabe für die Zubereitung benötigt, wenn ich nicht so viel Zeit habe.
Zitat von: Bitsqueezer am Januar 31, 2025, 13:57:01Arbeitsschritte würde ich allerdings 1:n definieren. Ich denke, es gibt nur wenige Rezepte, die die gleichen Arbeitsschritte wiederverwenden.
Z.B. ,,Zwiebeln (xyz) hacken" oder dito mit ,,schneiden" oder ,,dünsten".
Zitat von: Bitsqueezer am Januar 31, 2025, 13:57:01Man könnte sich hier überlegen, eine "Makrotabelle" zu erzeugen, um Basistexte zu hinterlegen, von denen man einen auswählen und in die Arbeitsschritte übernehmen zu können, dort aber noch anpassen zu können.
Das wäre dann wahrscheinlich mit VBA zu lösen?
Zitat von: Bitsqueezer am Januar 31, 2025, 13:57:01Die Tabelle Produkte würde ich hier vor allem an die ZutatID binden (also mit hinein). Denn ein Produkt "Motoröl" wird sicher nie an eine Zutat "Butter" gebunden werden.
Ich bin mir nicht ganz sicher, ob ich das richtig verstanden habe. Ich habe das mal so gelöst, wie im Bild dargestellt.
Zitat von: Bitsqueezer am Januar 31, 2025, 13:57:01Bei der Eingabe vereinfacht es sich dann auch: Wenn man Zutaten verwalten will, hat man ein Endlosformular mit der Zutatentabelle (1 Formular = 1 Tabelle), darin ein Unterformular, auf die Produkte-Tabelle als Endlosformular (wieder 1 Formular = 1 Tabelle), hier kann man dann auch gleich neue Produkte erfassen, die dann automatisch zu genau dieser Zutat gehören.
Du meinst wahrscheinlich ein übergeordnetes Formular mit einem Unterformular für die Tabelle ,,tblZutaten" und auf dem übergeordneten Formular ein weiteres Unterformular für die Tabelle ,,tblProdukte". Denn meines Wissens, kann ich in ein Endlosformular kein Unterformular einfügen.
Meinst du in der Tabelle ,,tblZutaten"? Da ist keine Einheit hinterlegt.
Zitat von: Bitsqueezer am Januar 31, 2025, 13:57:01Ich empfehle auch, die Namensgebung zu überdenken.
Ja, das ist nicht ideal. Bei dem Präfix mit den vorangestellten 5 Buchstaben der die Tabelle beschreibt werde ich bleiben, damit, wenn ich später mit Abfragen in der DB arbeite auch immer weis, welches Feld zu welcher Tabelle gehört. Aus dem Bildschirmfoto vom Beziehungsfenster ist das ersichtlich.
Screenshot 2025-01-31 060804.png
Hallo Konrad,
ZitatHier soll es darum gehen, dass ein Esslöffel Rama z.B. 10 Gramm und ein Esslöffel Erdnussbutter von Marke XYZ 8 Gramm wiegt. Das ist wichtig, wenn ich ein Rezept als Zutat für ein anderes Rezept haben und die Nährwerte berechnen will. Dann brauche ich immer das Zugabegewicht vom Produkt und dessen Nährwert pro 100 Gramm, damit ich die tatsächlich zugeführte Nährwertmenge berechnen kann.
Ja, das ist OK, wenn man unbedingt "Esslöffel" als Einheit speichern möchte. Da Du ja aber ohnehin ausrechnest, wieviel Gramm das ist für welche Zutat, würde ich das gleich als Gewicht speichern und Bezeichnungen wie "Esslöffel" oder "Prise" usw. im Freitext der Rezeptbeschreibung (bei den Arbeitsschritten) belassen, da diese keinerlei Informationswert für Gewichts- oder Nährwertberechnungen haben. Zumal "ein Esslöffel" oder "eine Prise" für unterschiedliche Menschen auch unterschiedliche Mengen bedeuten können.
ZitatZitat von: Bitsqueezer am Januar 31, 2025, 12:57:01
Eine Standard-Einheit wird in den Stammdaten der NährwertArt als EinheitID hinterlegt
Mir ist nicht ganz klar, was du mit Stammdaten meinst.
Die Tabelle "NährwertArt" ist eine Stammdatentabelle. Stammdaten sind solche, die man ständig braucht, aber die selbst so gut wie nie verändert werden. Wie etwa "Kalorien" - weder der Name wird sich ändern noch seine Bedeutung. Stammdatentabellen nennt man meistens auch "Lookup-Tabellen". Sie bestehen meistens aus nur wenigen Feldern, meistens ID, Name und Beschreibung (aber können natürlich auch viel mehr Felder haben).
ZitatZitat von: Bitsqueezer am Januar 31, 2025, 12:57:01
In die Produkttabelle gehören auch keine Gewichte. Das wird ja in der Zuordnung der Verpackungseinheit geregelt.
Das kann sein, aber wie sonst soll ich die Gewichte für die einzelnen Rezepteinheiten den Produkten zuordnen? Wenn ich die Nährwertmenge für das gesamte Rezept bestimmen will, dann muss ich wissen, wieviel z.B. ein EL von Rama oder Erdnussbutter wiegt, um ihn dann mit der durch 100 geteilten Nährwertangabe für den entsprechendn Nährwert multiplizieren zu können. Das mit allen Zutaten für das Rezept für alle Nährwerte, ergibt die gesamte Nährwertmenge für das Rezept.
Ein gutes Datenbankdesign zeichnet sich dadurch aus, daß eine Tabelle nur die absolut minimal notwendigsten Informationen zu einem Objekt beschreibt. Ein Produkt hat einen Produktnamen. Ein Produkt hat einen Hersteller. Wenn ich also "Schrauben" speichere, dann gibt es die gleiche Schraube in x unterschiedlichen Verpackungen und demnach Gewichten. Sobald ich also mehrere Daten der gleichen Art zu einem Item habe, dann gehören sie in eine eigene Tabelle. Also eine Liste von Verpackungen und -gewichten, die alle zum gleichen Produkt gehören. Oder in Datenbanksprache: 1:n. So einfach ist Datenbankdesign. Und wenn die exakt gleiche Schraube von zwei Herstellern hergestellt wird, dann ist die Beziehung zwischen Hersteller und Produkt auch keine 1:n-Beziehung mehr, sondern eine m:n-Beziehung. Ein Hersteller stellt m Produkte her und ein Produkt wird von n Herstellern hergestellt. Eine m:n ist auch immer wie eine 1:n handhabbar, denn wenn man auf einen Hersteller filtert, gibt es dazu n Produkte nur dieses Herstellers - und umgekehrt.
Das Datenbankdesign betrachtet dabei nicht, was Du später damit machen willst. Es gilt die Regel, Redundanzen zu vermeiden und sich in einer Tabelle auf ein konkretes Objekt zu beschränken, das mit vielen Attributen beschrieben werden kann - aber immer bezogen auf den konkreten Zweck, unabhängig von jeder anderen Tabelle.
Wenn man also eine Tabelle "Produkt" erstellt, interessiert es im Datenbankdesign nicht, ob Du ein ERP-System erstellst, in dem Du Produkte zum Bau verwalten willst, ob Du später eine Bestell- und Rechnungsverwaltung damit machen willst oder ob Du Produkte für Rezepte speicherst. In allen Fällen ist "Produkt" eine von ihrer Umgebung völlig unabhängige Tabelle, die für sich alleine so unabhängig designt wird, daß Du sie mit Copy/Paste in ein ERP-System oder eine Bestelldatenbank einfügen kannst und sofort verwenden kannst. Hier und da mal Anpassungen für bestimmte Regeln, aber die Idee des Datenbankdesigns bleibt immer gleich: Die kleinstmögliche Menge an Informationen, die exakt zu diesem einen Objekt gehören, hier das Produkt. "Objekt" meint dabei natürlich nicht automatisch ein physisches Objekt wie ein Produkt, sondern i.d.R. ein logisches Objekt, ein Gedankenkonstrukt. Diese Konzentration auf die konkreten Attribute eines Objektes führt genau dazu, daß man keine Redundanzen erzeugt (bzw. so wenig wie möglich, nicht immer vermeidbar und "sture Normalisierung" ist auch nicht in allen Fällen möglich oder sinnvoll).
Also zurück zur Frage: Das Gewicht ist einerseits etwas, was zum Produkt gehört - aber nicht zur Produktbeschreibung. Morgen wirft der Hersteller alles über den Haufen und hat statt 10,20,50 auf einmal 20,60,100 Gramm-Verpackungen. Die Verpackungen ändern aber nicht das geringste am Produkt: Der Name ändert sich nicht, es gibt keine Gewichtsangabe in einem Produkt, die Produktbeschreibung ändert sich nicht usw. Die Produkttabelle und ihre Daten bleiben von der Änderung komplett unbetroffen. Geändert werden nur die Verpackungsinformationen. Dabei würden im Beispiel die Datensätze 10,20,50 mit einem Gültigkeitsdatum (GültigBis) versehen werden, damit sie nicht gelöscht werden - denn die alten Datensätze werden vielleicht noch irgendwo verwendet und sollen weiterhin anzeigen, was einmal gespeichert gewesen ist und bei Aufruf zeigt "gültig bis" im Formular, daß man hier Anpassungen vornehmen muß, weil es diese Verpackungsgröße nicht mehr gibt (alternativ automatisiert man das und wenn sie nicht mehr verwendet werden, kann man sie dann löschen, außer man möchte noch nachsehen können, daß es sie mal gab).
Stammdaten sind hier also die Liste der Produkte, weil sie sich nur wenig ändern. Bewegungsdaten sind dann die Verpackungen mit ihren Gewichten. Generell ist die Grenze der Bezeichnungen aber fließend, denn man kann natürlich auch sagen, einmal eingegeben, nie mehr verändert, dann sind es eher Stammdaten und umgekehrt.
Und um auf die Rezepte zurückzukommen: Das Gewicht definiert einzig und allein die Rezeptdetails-Tabelle. Du hast die übergeordnete Rezepte-Tabelle, die beschreibt den Namen des Rezeptes und vielleicht eine Kategorie wie "vegan" usw. sowie einen Freitext - was auch immer. Viel mehr wird es nicht.
Du hast eine RezeptDetails-Tabelle, was nichts anderes als eine m:n-Tabelle ist, in der Du Informationen zum Rezept zusammenführst. Also welche Zutat wird mit welchem Gewicht benötigt? 5g Butter, also über die ausgewählte Zutat erhältst Du "Butter", die Zutat regelt die EinheitsID, also "g", und wenn Du die Produkte mit einem ZutatID-Feld versehen hast, dann gilt die ZutatID quasi als Kategorie für das Produkt. Was umgekehrt bei der Rezepteingabe auch gleich möglich macht, die bekannten (also der Datenbank bekannten) Produkte einer Zutat aufzuzeigen sowie deren Verpackungsgrößen (und Läden, die wir hier noch der Einfachheit halber weggelassen haben).
Wenn Du "1 Esslöffel" erfassen willst, dann hat ein Esslöffel Butter ein anderes Gewicht als 1 Esslöffel Mehl. Also ist die verbindende Einheit z.B. g oder ml (bei Flüssigkeiten). Warum soll ich das bei jedem Rezeptdetail erst eingeben? Mehl hat immer "g" und Milch hat immer "ml". Also gehört die Einheit fest einer Zutat zugeordnet, dann kann man sie im Frontend automatisch und unveränderlich anzeigen, ohne Bestandteil der RezeptDetails-Tabelle zu sein - und ohne sie eingeben zu müssen. Und wenn man dem User helfen möchte, dann definiert man eine Hilfstabelle, in der z.B. Größenordnungen wie "Prise" und "Esslöffel" und "gehäufter Teelöffen" und ähnliche solcher frei interpretierbarer "Mengen" über eine weitere m:n-Tabelle zum Produkt jeweils ein Gewicht zugeordnet wird, als "g" oder "ml" oder was auch immer. Die Einheit definiert hier wiederum die Zutat, das Produkt ist unabhängig von seinen Verpackungen, und das Gewicht wird in der m:n-Tabelle definiert. Also ID_Zutat,ID_Produkt, ID_Mengendefinition, Mengenwert (nicht "Gewicht", kann ja z.B. ml oder g sein oder was auch immer). In der Mengendefinitionstabelle stehen die o.g. Größeneinheiten als Stammdaten, also "Esslöffel" oder "gehäufter Teelöffel" oder "Prise". Die werden sich danach nie mehr ändern.
Diese Tabellen kann man dann im Frontend anbieten, wenn man das Rezept definieren will, um Größeneinheit auswählen zu können und Menge davon, also "Esslöffel" und "1". Die brauchen nicht gespeichert zu werden, denn sie werden im Frontend dann anhand der m:n-Tabelle in Gewicht umgerechnet und das Gewicht (besser: Mengenwert) wird in den Rezeptdetails gespeichert. Sie sind dann quasi nur ein "Textbaustein".
Wenn man später die Auswahl wieder anzeigen möchte, kann man die Auswahl "Esslöffel" und "1" natürlich zusätzlich speichern, aber nötig wäre es nicht. Entscheidend ist der Mengenwert, besonders für die Nährwertberechnung.
ZitatAlles klar. Aber wenn ich ein Rezept als Zutat verwenden will. Z.B. Mehl aus Leinsamen, Sonnenblumenkernen und Mandeln, dann muss ich wissen, was ein EL davon wiegt und dafür benötige ich das Gewicht für einen EL für dieses Rezept
Das wäre damit ja bereits beschrieben.
ZitatZitat von: Bitsqueezer am Januar 31, 2025, 12:57:01
Arbeitsschritte würde ich allerdings 1:n definieren. Ich denke, es gibt nur wenige Rezepte, die die gleichen Arbeitsschritte wiederverwenden.
Z.B. "Zwiebeln (xyz) hacken" oder dito mit "schneiden" oder "dünsten".
Zitat von: Bitsqueezer am Januar 31, 2025, 12:57:01
Man könnte sich hier überlegen, eine "Makrotabelle" zu erzeugen, um Basistexte zu hinterlegen, von denen man einen auswählen und in die Arbeitsschritte übernehmen zu können, dort aber noch anpassen zu können.
Das wäre dann wahrscheinlich mit VBA zu lösen?
Wie in dem Beispiel mit den Esslöffeln: Man definiert sich solche Textbausteine, die dann z.B. einen Eintrag "Zwiebel hacken" enthalten, und diese kann man dann in ein Arbeitsschritt-Freitext-Feld (per VBA) einfügen, also etwa an das Ende des bestehenden Textes anfügen, so daß der Benutzer es anpassen kann. Also etwa zu "Zwiebel besonders fein hacken". Du willst ganz sicher nicht JEDE Sonderformulierung als Lookup-Tabelle definieren, dann dauert auch die Suche nach dem richtigen Eintrag ewig lang. Der Arbeitsschritt an sich ist auch nicht sonderlich wichtig für Auswertungen. Daher ist hier Freitext genau richtig.
Nebenbei würde ich hier auch die Zeit für einen Arbeitsschritt unterbringen. Damit entfällt die Zeitangabe im Rezept selbst, das Rezept kann dann durch Aufsummieren der Arbeitsschritt-Zeiten errechnet werden - und Du kannst die Arbeitsschritte, einmal mit Zeit definiert, auch für andere Rezepte wiederverwenden und mußt dann nicht erneut überlegen, wie lange der Schritt kostet. Die Zwiebel zu hacken, wird in jedem anderen Rezept sicherlich nicht unterschiedlich viel Zeit kosten. Wieder eine Eingabe gespart, wieder mehr Details in der Datenbank. Zum Beispiel abzufragen, welche Rezepte besonders viel Zeit für passive Tätigkeiten wie backen brauchen und welche besonders viel Zeit für aktive wie "Zwiebel hacken". Was man mit einer Gesamtzeit nicht ermitteln kann.
Wieder das Prinzip, zusammengehörende Daten in eine Tabelle. Der Arbeitsschritt kostet Zeit. Das Rezept mit der Zutatenliste kostet keine Zeit. Die Zutatenliste soll die Zutaten listen (ach.. :) ) und die Arbeitsschritte sind es, die Zeit kosten.
ZitatZitat von: Bitsqueezer am Januar 31, 2025, 12:57:01
Die Tabelle Produkte würde ich hier vor allem an die ZutatID binden (also mit hinein). Denn ein Produkt "Motoröl" wird sicher nie an eine Zutat "Butter" gebunden werden.
Ich bin mir nicht ganz sicher, ob ich das richtig verstanden habe. Ich habe das mal so gelöst, wie im Bild dargestellt.
Habe ich ja hier bereits beschrieben: In die Tabelle ProduktID gehört die ZutatID, da es wie eine Kategorie zu sehen ist. Produkt "Landliebe Butter" und "Kerrygold Butter" ist jeweils Kategorie/Zutat "Butter".
ZitatZitat von: Bitsqueezer am Januar 31, 2025, 12:57:01
Bei der Eingabe vereinfacht es sich dann auch: Wenn man Zutaten verwalten will, hat man ein Endlosformular mit der Zutatentabelle (1 Formular = 1 Tabelle), darin ein Unterformular, auf die Produkte-Tabelle als Endlosformular (wieder 1 Formular = 1 Tabelle), hier kann man dann auch gleich neue Produkte erfassen, die dann automatisch zu genau dieser Zutat gehören.
Du meinst wahrscheinlich ein übergeordnetes Formular mit einem Unterformular für die Tabelle ,,tblZutaten" und auf dem übergeordneten Formular ein weiteres Unterformular für die Tabelle ,,tblProdukte". Denn meines Wissens, kann ich in ein Endlosformular kein Unterformular einfügen.
Ja, das ist leider ein uraltes Problem der Falschmeldung von Access. Beim Versuch bekommst Du immer die (falsche) Information, daß man kein UFo in ein Endlosformular einbauen kann, was aber Unsinn ist. Du kannst keins im Detailbereich einfügen, aber sehr wohl im Kopf-/Fußbereich eines Formulares. Wenn Du es versuchst, wird Access Dein Endlosformular nach der Meldung auf Einzelformular umstellen. Einfach ignorieren und wieder auf Endlosformular umstellen - fertig. Schon hast Du im Detailbereich die Endlosliste und bei Auswahl einer Zeile im z.B. Fußbereich die zugehörigen Daten. Das kannst Du auch verschachteln, abhängig von der Bildschirmgröße. Also das UFo kann seinerseits einen Fußbereich haben mit einem weiteren UFo. Funktioniert problemlos. Man sollte es nur nicht übertreiben und vor allem kompakt designen, sonst wird aus dem "Endlosformular" ein Einzeiler... :)
Beispiel, wie sowas aussehen kann, in dem Beitrag hier in der Ausweis-Demodatenbank: https://www.access-o-mania.de/forum/index.php?msg=165961
ZitatMeinst du in der Tabelle "tblZutaten"? Da ist keine Einheit hinterlegt.
Ja, stimmt, die fehlt dort ja noch, wie nun hier ja ausführlich beschrieben.
ZitatZitat von: Bitsqueezer am Januar 31, 2025, 12:57:01
Ich empfehle auch, die Namensgebung zu überdenken.
Ja, das ist nicht ideal. Bei dem Präfix mit den vorangestellten 5 Buchstaben der die Tabelle beschreibt werde ich bleiben, damit, wenn ich später mit Abfragen in der DB arbeite auch immer weis, welches Feld zu welcher Tabelle gehört.
Das ist ja genau der Irrglaube. Sowohl in SQL und auch im Abfragedesigner sieht Du es IMMER genau:
SELECT MeineTabelle.MeinFeld
FROM MeineTabelleEs ist also klar, daß "MeinFeld" aus "MeineTabelle" kommt, oder nicht? Im Abfragedesigner steht der Tabellenname unter dem Feld.
Was Du machst ist:
SELECT MeineTabelle.MeineTabelleMeinFeld
FROM MeineTabelleAlso welchen Mehrwert bietet Dir nun "MeineTabelleMeinFeld"? Keinen, es verlängert und verkompliziert nur den Feldnamen, ist grundsätzlich immer überflüssig.
Zum Datenmodell:
Laden UND Marke der m:n hinzuzufügen, ist wieder Redundanz. Die Beschreibung der Verpackungseinheit zum Produkt ist die Information, die in eine Tabelle gehört. Mit dem Laden fügst Du wieder überflüssige Redundanz ein, denn jetzt mußt Du für "Rewe", "Lidl", "Aldi" drei Datensätze mit den gleichen Informationen einfügen, nur mit unterschiedlichen Läden.
Produkt zu Verpackung ist die Information, die diese Tabelle beschreiben soll. Diese Tabelle hat eine eigene ID, und diese verlinkt man in einer weiteren m:n zu den Läden. Also PdouktZuVerpackungID zu LadenID. So kann man in drei Zeilen dieser weiteren m:n-Tabelle alle Läden erfassen, die genau dieses Produkt in genau dieser Verpackungsgröße anbieten. Ansonsten müßtest Du bei einer Auflistung aller Verpackungsgrößen eines Produktes immer alle Duplikate ausfiltern. Im gegenwärtigen Modell würdest Du drei Zeilen mit der gleichen Verpackungsgröße definieren, obwohl es immer die gleiche Verpackungsgröße ist. Was zusätzlich zu Eingabefehlern führen kann. Wenn ich also später vom Laden unabhängig die Verpackungsgrößen eines Produktes listen will, ergeben sich 3 Zeilen aus dieser Tabelle, wenn man den Laden wegläßt, hast Du drei gleiche Zeilen. Die müßte man dann mit GROUP BY oder DISTINCT erst wieder zu einer Zeile machen. Das ist eben Redundanz. Das Datenbankdesign hat grundsätzlich nichts mit den Formularen zu tun und dem Benutzerkomfort.
Redundanz hast Du prinzipiell auch in Deiner Nährwerttabelle, denn alle Felder hier sind wiederum Aufzählungsfelder. Du hast als Beispiel 2 Felder mit "Energie", zwei Felder mit "Fett".
Schau Dir mal eine einfache Nährwerttabelle an:
https://www.dge-medienservice.de/media/productattach/File-1560509394.pdf
Da ergibt sich bereits einen ganzen Haufen an Definitionen, die Du mit Deiner Tabelle so nie abdecken kannst. Die Darstellung dieser PDF ist bereits eine PivotDarstellung, und muß für den Datenbankdesigner in einzelne Tabellen aufgetrennt werden, wenn man diese sinnvoll verwalten will.
Im Allgemeinen ist die Einheit hier "g", aber ebenso "Mikrogramm" oder "kcal" oder "kcal/g". Es gibt Kategorien wie "Mineralstoffe", "Vitamine", "Energie", "Eiweiß", "Kolenhydrate", "NaCl", "Alkohol", "Fett", "Wasser", die wiederum Unterkategorien aufweisen. Diese werden je nach Produktkategorie wie "Fleisch/Fisch" und "Getränke" usw. unterschiedlich verwendet, was also, wenn man es korrekt handhaben will, einige zusätzliche Tabellen und Beziehungen braucht, um produktunabhängig Nährwerte zu verwalten. Dann kann man sich überlegen, ob man diese einer Zutat zuordnen will wie "Butter" (wie es auch die PDF macht), oder spezifischer den Herstellerangaben zu einem Produkt.
Da es halt doch sehr viele und sehr unterschiedliche Arten von Nährwertbetrachtung gibt, reicht Deine Tabelle hier nicht aus. Ein Getränk ist anders zu bewerten wie Butter. Entsprechend muß man die Art des Nährwertes auswählen können (wie "Energie: kcal" oder "Energie: kJ", was zwei Zeilen in der Tabelle sind), den Wert und die Einheit.
In Deiner Tabelle könntest Du z.B. keine Mineralstoffe und keine Vitamine verwalten.
Kleiner Tip am Rande: Vermeide Tabellennamen wie "tblKategorie" oder "tblArt" usw. Das sagt absolut nichts über den Inhalt aus und Kategorien gibt es, wie man sieht, an allen Ecken und Enden einer Datenbank. Es sollte immer im Namen zu sehen sein, zu was die Kategorie gehört.
Das war's erst mal wieder dazu.
Vergiß auf jeden Fall erst mal die Formularerstellung, solange das Datenmodell noch nicht vollständig und durchdacht ist. Darüber macht man sich erst zum Schluß Gedanken.
Ich denke, das Prinzip habe ich nun ausführlich beschrieben, Du solltest dann mal entsprechende Tutorials durchgehen, da ich hier keinen Datenbankdesignkurs geben kann.
Gruß
Christian
Hallo Christian,
Danke für deine Mühe bis hierhin.
Zitat von: Bitsqueezer am Februar 01, 2025, 15:24:16Ich denke, das Prinzip habe ich nun ausführlich beschrieben, Du solltest dann mal entsprechende Tutorials durchgehen, da ich hier keinen Datenbankdesignkurs geben kann.
Das werde ich erst mal machen und dann deine Tips durcharbeiten.