1. Willkommen im Forum für alle Datenbanken! Registriere Dich kostenlos und diskutiere über DBs wie Mysql, MariaDB, Oracle, Sql-Server, Postgres, Access uvm
    Information ausblenden

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

Dieses Thema im Forum "Datenmodellierung, Datenbank-Design" wurde erstellt von tomlener, 6 Dezember 2016.

  1. tomlener

    tomlener Neuer Benutzer

    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
     
  2. tomlener

    tomlener Neuer Benutzer

    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.
     
  3. akretschmer

    akretschmer Datenbank-Guru

    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.
     
    tomlener gefällt das.
  4. tomlener

    tomlener Neuer Benutzer

    @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
     
    akretschmer gefällt das.
  5. akretschmer

    akretschmer Datenbank-Guru

    Da kann man schon mal 'Gefällt mir' klicken ;-)
     
Die Seite wird geladen...

Diese Seite empfehlen

  1. Diese Seite verwendet Cookies. Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies.
    Information ausblenden