Neuigkeiten:

Ist euer Problem gelöst, dann bitte den Knopf "Thema gelöst" drücken!

Mobiles Hauptmenü

Fehlermeldung beim Abfangen von Fehler 3022

Begonnen von KonradR, Januar 26, 2025, 06:37:56

⏪ vorheriges - nächstes ⏩

KonradR

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:
Sie dürfen in diesem Board keine Dateianhänge sehen.
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?

knobbi38

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



KonradR

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:
Sie dürfen in diesem Board keine Dateianhänge sehen.

und vom Formular:
Sie dürfen in diesem Board keine Dateianhänge sehen.
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.

knobbi38

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


MzKlMu

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.
Gruß Klaus

KonradR

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.

KonradR

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?

MzKlMu

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.
Gruß Klaus

knobbi38

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

Bitsqueezer

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

KonradR

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.


KonradR

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


Bitsqueezer

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

KonradR

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:
Sie dürfen in diesem Board keine Dateianhänge sehen.
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?



Bitsqueezer

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