Anfängerfrage: wie würdest du diese Aufgabe lösen?

tomlener

Neuer Benutzer
Beiträge
3
Hallo und schonmal Danke für deine Zeit, dich kurz mit dieser Anfängeraufgabe zu beschäftigen.

In der Datenbank sind gespeichert:

Tabelle Produkte
id, name
1, apfel
2, birne
3, zwetschke
4, traube
5, kürbis
6, marille

Nun möchte ich Verbindungen erstellen, zum Beispiel

Apfel, Zwetschke und Marille gehören zusammen

Birne und Kürbis gehören zusammen

Traube gehört zu niemanden / niemand gehört zu Traube

Ziel:
Wenn die Detailseite "Apfel" aufgerufen wird, werden dort Zwetschke und Marille als zugehörig angezeigt.
Wenn die Detailseite "Marille" aufgerufen wird, werden dort Apfel und Zwetschke als zugehörig angezeigt.

Es gibt eine nach oben offene Anzahl an Produkten, und eine unbekannte Zahl von verschiedenen Zugehörigkeiten. Wobei Zugehörigkeiten immer in sich geschlossene Gruppen sind. Im obigen Beispiel könnte Apfel niemals zugehörig sein zu Birne (und umgekehrt).

Mir ist schon klar, dass diese Aufgabe für einen Profi larifari ist, aber ich als Anfänger bin für jeden Lösungsansatz wie dies elegant zu lösen ist sehr dankbar!

Grüße Tom
 
Werbung:
Zusatz: meine Lösung wäre, eine Spalte "gehörtzu" hinzuzufügen. Wenn ein Benutzer ein Produkt einem anderen als zugehörig speichert, starte eine Abfrage per Programmcode ob eines der beiden Produkte bereits einen Eintrag in "gehörtzu" hat. Falls ja, diesen auch beim anderen Produkt, welches diesen Eintrag noch nicht hat, speichern. Falls keines der beiden Produkte einen Eintrag in "gehörtzu" hat, eine zufällige Zeichenkette generieren und bei beiden Produkten in "gehörtzu" speichern.

Aber ich finde diese Lösung nicht gut. Deshalb dieser Forenpost.
 
Du kannste das 'klassisch' lösen mit einer Zuordnungstabelle.

Code:
test=*# \d produkte;
  Tabelle »public.produkte«
 Spalte |  Typ  | Attribute
--------+---------+-----------
 id  | integer | not null
 name  | text  |
Indexe:
  "produkte_pkey" PRIMARY KEY, btree (id)
Fremdschlüsselverweise von:
  TABLE "p_verbindungen" CONSTRAINT "p_verbindungen_mitglied_fkey" FOREIGN KEY (mitglied) REFERENCES produkte(id)

test=*# \d p_verbindungen
Tabelle »public.p_verbindungen«
  Spalte  |  Typ  | Attribute
----------+---------+-----------
 gruppe  | integer | not null
 mitglied | integer | not null
Indexe:
  "p_verbindungen_pkey" PRIMARY KEY, btree (gruppe, mitglied)
Fremdschlüssel-Constraints:
  "p_verbindungen_mitglied_fkey" FOREIGN KEY (mitglied) REFERENCES produkte(id)

test=*# select * from produkte;
 id |  name   
----+-----------
  1 | apfel
  2 | birne
  3 | zwetschke
  4 | traube
  5 | kürbis
  6 | marille
(6 Zeilen)

test=*# select * from p_verbindungen;
 gruppe | mitglied
--------+----------
  1 |  1
  1 |  3
  1 |  6
(3 Zeilen)

test=*# explain select * from p_verbindungen where gruppe in (select gruppe from p_verbindungen where mitglied = 1);
  QUERY PLAN   
-----------------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=0.26..24.38 rows=3 width=8)
  Join Filter: (p_verbindungen.gruppe = p_verbindungen_1.gruppe)
  ->  Index Only Scan using p_verbindungen_pkey on p_verbindungen p_verbindungen_1  (cost=0.13..12.16 rows=1 width=4)
  Index Cond: (mitglied = 1)
  ->  Index Only Scan using p_verbindungen_pkey on p_verbindungen  (cost=0.13..12.18 rows=3 width=8)
(5 Zeilen)

test=*# select * from p_verbindungen where gruppe in (select gruppe from p_verbindungen where mitglied = 1);
 gruppe | mitglied
--------+----------
  1 |  1
  1 |  3
  1 |  6
(3 Zeilen)

test=*#

In p_verbindungen hast Du Gruppen und deren Mitglieder, für Dein Beispiel Gruppe 1 mit den Produkt-Mitgliedern 1,3 und 6. Die Abfrage liefert Dir bei z.B. Apfel=1 als Produkt die Mitglieder der Gruppe, Dank Index bekommst das auch indexiert gesucht.

Aber Du wolltest eine elegante Lösung, so here we go:

Code:
test=*# \d prod_verbindung
prod_verbindung  prod_verbindung_pkey  
test=*# \d prod_verbindung;
 Tabelle »public.prod_verbindung«
  Spalte  |  Typ  | Attribute
----------+-----------+-----------
 id  | integer  | not null
 produkte | integer[] |
Indexe:
  "prod_verbindung_pkey" PRIMARY KEY, btree (id)
  "idx_verbindungen" gin (produkte)

test=*# select * from prod_verbindung ;
 id | produkte
----+----------
  1 | {1,3,6}
(1 Zeile)

test=*#

Ich hab also eine Tabelle mit einem ARRAY, welches alle Produkte einer Gruppe enthält. Die Anfrage sieht dann so aus und kann Danke des GIN-Indexes hier auf dem Array auch via Indexsuche ablaufen:

Code:
test=*# explain analyse select *, array_remove(produkte, 1) as andere_gruppenmitglieder from prod_verbindung where produkte @> array[1];
  QUERY PLAN   
--------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on prod_verbindung  (cost=12.05..21.53 rows=6 width=36) (actual time=0.018..0.019 rows=1 loops=1)
  Recheck Cond: (produkte @> '{1}'::integer[])
  Heap Blocks: exact=1
  ->  Bitmap Index Scan on idx_verbindungen  (cost=0.00..12.05 rows=6 width=0) (actual time=0.009..0.009 rows=1 loops=1)
  Index Cond: (produkte @> '{1}'::integer[])
 Planning time: 0.068 ms
 Execution time: 0.041 ms
(7 Zeilen)

test=*# select *, array_remove(produkte, 1) as andere_gruppenmitglieder from prod_verbindung where produkte @> array[1];
 id | produkte | andere_gruppenmitglieder
----+----------+--------------------------
  1 | {1,3,6}  | {3,6}
(1 Zeile)

test=*#

In der ersten Spalte quasi Deine Produktgruppe, in der zweiten alle Mitglieder dieser Gruppe, in der dritten habe ich den Apfel mal schon rausgenommen, damit Du alle anderen Mitglieder siehst, nicht aber das aktuell abgefragte.
 
@akretschmer: das gefällt mir richtig gut, und für mich persönlich bietet dieses Beispiel auch spannendes Neuland und Lerninhalt.

Es ist in Foren sehr selten, so ausführliche und hilfreiche Antworten zu erhalten.

Lass Dir herzlichst Danken, dass Du Dein Talent mit dem Forum teilst!

Grüße
Tom Lener
 
Werbung:
Zurück
Oben