Nächste Überarbeitung | Vorhergehende Überarbeitung |
modul:m290_guko:learningunits:lu08:theorie:e_many-to-many [2025/10/19 22:55] – angelegt gkoch | modul:m290_guko:learningunits:lu08:theorie:e_many-to-many [2025/10/19 23:01] (aktuell) – gkoch |
---|
(Die Verknüpfung **users ↔ posts** ist 1:N; **posts ↔ categories** ist N:M.) | (Die Verknüpfung **users ↔ posts** ist 1:N; **posts ↔ categories** ist N:M.) |
| |
==== Warum N:M auflösen? (kurz) ==== | ==== Warum N:M auflösen? ==== |
In einer relationalen Tabelle zeigt ein Fremdschlüssel **immer nur auf genau eine** Zeile. | In einer relationalen Tabelle kann ein Fremdschlüssel **immer nur auf genau eine** Zielzeile verweisen. |
Bei **N:M** gilt jedoch: **ein Post → mehrere Kategorien** und **eine Kategorie → mehrere Posts**. | Bei **N:M** gilt jedoch: **ein Post → mehrere Kategorien** und **eine Kategorie → mehrere Posts**. |
Das lässt sich **nicht** mit einem einzigen FK-Feld abbilden: | Das lässt sich mit **einem** FK-Feld nicht sauber abbilden: |
| |
- FK in ''posts'' nur als ''category_id'': Pro Post wäre nur eine Kategorie möglich. Mehrere Kategorien erzwingen Duplikate desselben Posts – Änderungen und Löschungen werden fehleranfällig. | * FK in ''posts'' nur als ''category_id'': Pro Post wäre **nur eine** Kategorie möglich. Mehrfachzuordnungen erzwingen **Duplikate desselben Posts** → Änderungen/Löschungen an mehreren Stellen, fehleranfällig. |
- FK in ''categories'' nur als ''post_id'': Pro Kategorie wäre nur ein Post möglich. Dadurch müsste man Kategorien duplizieren – gleiches Problem wie bei obigen Punkt. | * FK in ''categories'' nur als ''post_id'': Pro Kategorie wäre **nur ein** Post möglich. Für mehrere Posts müsste man Kategorien **duplizieren** → Eindeutigkeit geht verloren. |
- Kommaliste in einer Spalte (z. B. ''Belgien, Städtereise''): In einer Zelle stehen mehrere Werte. Die Datenbank kann nicht prüfen, ob diese Kategorien existieren (kein FK), Abfragen werden unzuverlässig und langsam. | * Kommaliste in einer Spalte (z. B. ''Belgien, Städtereise''): In **einer Zelle stehen mehrere Werte**. Die DB kann nicht prüfen, ob diese Kategorien existieren (kein FK); Abfragen werden **unzuverlässig** und **langsam**. |
- mehrere FK-Spalten (z.B. category_id1, category_id2 etc.) pro Post oder Kategorie: Das limitiert die Anzahl mögliche Kategorien, die einem Post zugewiesen können (limitiert die Anzahl an Beziehungen, da bei der Tabellendefinition die Spalten definiert werden müssen. | * Mehrere FK-Spalten (z. B. ''category_id1'', ''category_id2'' …): Die Anzahl möglicher Zuordnungen ist **künstlich begrenzt**; das Schema müsste bei neuen Fällen **ständig erweitert** werden. |
| |
Gleich hier ein paar Tabellen, die die einzelnen vorhin aufgeführten Punkte visualisieren **alles sind schlechte Lösungen** (s. obige Punkte): | Die folgenden Tabellen-Skizzen visualisieren diese Ansätze – **alles sind schlechte Lösungen** (siehe oben): |
| |
{{:modul:m290_guko:learningunits:lu08:theorie:many-to-many_not-resolved.png?direct&800|}} | |
| |
| {{:modul:m290_guko:learningunits:lu08:theorie:many-to-many_not-resolved.png?direct&800|}} |
| |
**Lösung: die Zwischentabelle ''post_category''.** | **Lösung: die Zwischentabelle ''post_category''.** |
Wir trennen die Objekte (''posts'', ''categories'') und speichern **jede einzelne Zuordnung** als eigene Zeile in ''post_category'' – genau **ein Paar** (''post_id'', ''category_id'') pro Zeile. | Wir trennen die Objekte (''posts'', ''categories'') und speichern **jede einzelne Zuordnung** als eigene Zeile in ''post_category'' – genau **ein Paar** (''post_id'', ''category_id'') pro Zeile. |
So bleibt jede Beziehung eindeutig modelliert, Primärschlüssel bleiben eindeutig, Fremdschlüssel sind prüfbar, und Abfragen bleiben klar und performant. | So bleibt jede Beziehung **eindeutig** modelliert, Primärschlüssel bleiben eindeutig, Fremdschlüssel sind **prüfbar**, und Abfragen bleiben **klar** und **performant**. |
| |
=== Visualisierung (Kontext) === | === 1:N vs N:M mit Zwischentabelle === |
**users ↔ posts (1:N)** | **users ↔ posts (1:N)** |
{{:modul:m290_guko:learningunits:lu08:theorie:reiseblog-connected_users-posts.png?direct&1600|}} | {{:modul:m290_guko:learningunits:lu08:theorie:reiseblog-connected_users-posts.png?direct&1600|}} |
===== Zusammenfassung ===== | ===== Zusammenfassung ===== |
| |
* **N:M** wird **immer** über eine **Zwischentabelle** mit **einer Paarung pro Zeile** modelliert: //(post_id, category_id)//. | * **N:M** wird **immer** über eine **Zwischentabelle** mit **einer Paarung pro Zeile** modelliert: //(post_id, category_id)//. |
* **Fremdschlüssel** sichern die Datenqualität; mit **ON DELETE CASCADE** werden Zuordnungen automatisch aufgeräumt. | * **Fremdschlüssel** sichern die Datenqualität; mit **ON DELETE CASCADE** werden Zuordnungen automatisch aufgeräumt. |
| |
<WRAP tip round 80% center> | <WRAP tip round 80% center> |
**Ausblick auf LU09:** Wir formulieren dieselben Abfragen für mehrere Tabellen </WRAP> | **Ausblick auf LU09:** Wir formulieren dieselben Abfragen für mehrere Tabellen </WRAP> |
| |