Alex: Entscheidungshilfe zur Datenbank-Struktur

Hallo,

wollte mal fragen ob Ihr mir eine Datenbank-Struktur empfehlen könnt.
Und zwar will ich z.B. viele Produkte in einer Datenbank speichern. Diese Produkte können von insgesamt 30 Kategorien in drei Kategorien eingeteilt werden. Also jedes Produkt kann vom Benutzer in maximal drei verschiedene Kategorien eingeteilt werden, von insgesamt 30 Kategorien.
Beim Auflisten der Produkte können alle Kategorien ausgewählt werden.

Zum besseren Verständnis:
Produkt 1 -> Kategorie a,b
Produkt 2 -> Kategorie a,c
Produkt 3 -> Kategorie a,b,c
Produkt 4 -> Kategorie a,g,h

Auflistung
ausgewählte Kategorien: b,c,d,e,f,k,x,z
angezeigte Produkte
Produkt 1-2-3

Jetzt hab ich die Möglichkeit in der Datenbank (Mysql) für jedes Produkt eine einzige Kategoriespalte anzulegen und in dieser dann die ausgewählten Kategorien abzuspeichern. z.B. in diesem Format a-b oder a-c oder a-g-h
Und bei der Auflistung kann ich dann die Abfrage mit cat like "%b%" or cat like "%c%" ... machen.

Die zweite Möglichkeit wäre bei jedem Produkt zusätzlich 30 Spalten für die Kategorien anzulegen und dann z.B. die ausgewählten Kategorien auf 1 zu setzen. Und bei der Auflistung dann mit "where cat_a=1 or cat_b=1 ..."

Bis jetzt wollte ich die Abfrage mit like machen um mir die vielen Spalten in der Tabelle zu ersparen. Da ich aber gelesen habe, dass es nicht selten vorkommt, dass Tabellen aus 100 oder mehr Spalten bestehen, frag ich mich jetzt, ob ich anstatt nur einer Spalte für die Kategorien mehrere Spalten nutzen soll.

Vorallem is es so, dass ich Abfragen mit like, aus Performance-Gründen eigentlich nicht so gern mache, aber andererseits besteht die Tabelle dann aus ca. 80 Spalten, weil zu den Kategorien (30) noch andere Kriterien (4x10) hinzukommen.

Also ums auf den Punkt zu bringen:
Wenig Spalten und dafür mit "like" oder viele spalten mit "cat_x=1"

Die Tabelle würde aus ca. 10.000 Datensätzen bestehen.

mfg
Alex

  1. Lieber Alex,

    wäre hier nicht eine n:m-Beziehung sinnvoll? Eine Tabelle, die Produkt-ID mit Kategorien-ID in Verbindung bringt? Dazu könntest Du feststellen, dass es nur drei Einträge mit der jeweiligen Produkt-ID pro User gibt.

    Liebe Grüße,

    Felix Riesterer.

    --
    ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
    1. Mahlzeit Felix Riesterer,

      wäre hier nicht eine n:m-Beziehung sinnvoll?

      Ja. Nur und ausschließlich.

      (Schließlich könnte sich z.B. die fachliche Anforderung, dass jedes Produkt maximal drei Kategorien zugeordnet sein darf, im Nachhinein ändern o.ä. ...)

      Lesetipp (für Alex): Normalisierung

      MfG,
      EKKi

      --
      sh:( fo:| ch:? rl:( br:& n4:~ ie:% mo:} va:) de:] zu:) fl:{ ss:) ls:& js:|
      1. Hallo,

        hab mir das mit der n:m-Beziehung näher angeschaut.

        So wie ich das jetzt verstanden habe, wäre die folgende Datenbank-Struktur besser geeignet.

        Produkte
        ProduktId - ProduktName - ProduktPreis ...
        123450
        123451
        123452

        ProduktKategorie
        ProduktId - KategorieId
        123450      10
        123450      11
        123451      10
        123451      12
        123452      10
        123452      14
        123452      16

        Und eine Abfrage wie diese:
        select Produkte.ProduktId,ProduktName,ProduktPreis from Produkte,ProduktKategorie where Produkte.ProduktId=ProduktKategorie.ProduktId and ProduktKategorie.KategorieId in (10,11,14,20,21) group by ProduktKategorie.KategorieId order by ProduktPreis limit 30;

        mfg
        Alex

        1. Hi!

          hab mir das mit der n:m-Beziehung näher angeschaut.

          (Wird eigentlich andersrum benannt, also m:n-Beziehung.)

          So wie ich das jetzt verstanden habe, wäre die folgende Datenbank-Struktur besser geeignet.

          Ja.

          Und eine Abfrage wie diese:
          select Produkte.ProduktId,ProduktName,ProduktPreis from Produkte,ProduktKategorie where Produkte.ProduktId=ProduktKategorie.ProduktId and ProduktKategorie.KategorieId in (10,11,14,20,21) group by ProduktKategorie.KategorieId order by ProduktPreis limit 30;

          Was genau willst du abfragen? Die Abfrage, so wie du sie formuliert hast, geht nur mit MySQL, weil alle anderen DBMSe sich dagegen wehren, Felder anzufragen (in SELECT) über die nicht gruppiert oder eine Aggregatfunktion angewendet wurde. MySQL lässt das zu, nimmt dann aber irgendeinen undefinierten Datensatz. Die Abfrage dürfte also ein zufälliges Produkt aus jeder der Kategorien ergeben. Das LIMIT ist auch wirkungslos, weil nur 5 Kategorien gesucht werden.

          Wenn du alle Produkte der vorgegebenen Kategorien haben möchtest, dann ist der erste Schritt, alle Produktnummern zu ermitteln:

          SELECT ProduktId FROM ProduktKategorie WHERE KategorieId IN (10,11,14,20,21)

          Jetzt kommt die Produkte-Tabelle ins Spiel:

          SELECT angaben FROM Produkte WHERE ProduktId IN (...)

          Anstelle der Punkte kommt die obige Abfrage in zusätzlichen ()-Klammern hin. Das ist dann eine Subquery, die braucht ihre eigenen Klammern und das IN() ebenfalls.

          Lo!

          1. Hallo!

            (Wird eigentlich andersrum benannt, also m:n-Beziehung.)

            Hmm, bei Wikipedia steht n:m und google findet auch überwiegend n:m. Egal.

            Was genau willst du abfragen? Die Abfrage, so wie du sie formuliert hast, geht nur mit MySQL, weil alle anderen DBMSe sich dagegen wehren, Felder anzufragen (in SELECT) über die nicht gruppiert oder eine Aggregatfunktion angewendet wurde. MySQL lässt das zu, nimmt dann aber irgendeinen undefinierten Datensatz. Die Abfrage dürfte also ein zufälliges Produkt aus jeder der Kategorien ergeben. Das LIMIT ist auch wirkungslos, weil nur 5 Kategorien gesucht werden.

            Also ich möchte die Produkte bzw. die dazugehörigen Informationen abfragen, unter Berücksichtigung der ausgewählten Kategorien.

            Das "limit 30" am Ende bezieht sich nicht auf die 5 Kategorien sondern auf die Anzahl der Produkte die ausgelesen werden sollen.

            So wie ich die Abfrage hier geschrieben habe funktioniert es eigentlich so wie es soll. Die Frage ist welche Abfrage nun schneller abgearbeitet werden kann.

            mfg
            Alex

            1. Hi!

              (Wird eigentlich andersrum benannt, also m:n-Beziehung.)
              Hmm, bei Wikipedia steht n:m und google findet auch überwiegend n:m. Egal.

              Es hat dort jemand einheitlich auf n:m geändert, früher waren wohl beide Schreibweisen im Artikel. Ein "richtig" scheint es dabei wohl nicht zu geben. Ich finde m:n besser, weil wie beim 1:n das n rechts steht.

              Also ich möchte die Produkte bzw. die dazugehörigen Informationen abfragen, unter Berücksichtigung der ausgewählten Kategorien.
              Das "limit 30" am Ende bezieht sich nicht auf die 5 Kategorien sondern auf die Anzahl der Produkte die ausgelesen werden sollen.

              Schon klar wie das Limit beabsichtigt ist, aber mit der Gruppierung bekommst du nur einen Datensatz je Gruppe. Da die Kategorien auf 5 beschränkt sind, sollten auch nur 5 Gruppen gebildet werden können und damit 5 Datensätze im Ergebnis stehen.

              So wie ich die Abfrage hier geschrieben habe funktioniert es eigentlich so wie es soll.

              Das kann ich mir nicht vorstellen, oder du hast dein geschildertes Ergebnis mit einem anderen Statement bekommen. Problematisch ist die Abfrage mit dieser Art der Gruppierung sowieso. Manche behaupten auch, das sein schlichtweg falsch, dass MySQL solcherart gruppierte Abfragen zulässt.

              Die Frage ist welche Abfrage nun schneller abgearbeitet werden kann.

              Die lässt sich nicht so einfach beantworten. Die Geschwindigkeit hängt von vielen Kriterien ab. Wesentliche Beschleuniger sind üblicherweise Indexe. Die werden aber vom Optimizer mal verwendet und mal nicht. Ausschlaggebend sind dabei unter anderem der Umfang der bei einer Abfrage entstehenden Datenmenge und die Kardinalität des Indexes.

              Lo!

  2. Und bei der Auflistung kann ich dann die Abfrage mit cat like "%b%" or cat like "%c%" ... machen.

    Das ist unperformant und absolut unschön.

    Die zweite Möglichkeit wäre bei jedem Produkt zusätzlich 30 Spalten für die Kategorien anzulegen und dann z.B. die ausgewählten Kategorien auf 1 zu setzen. Und bei der Auflistung dann mit "where cat_a=1 or cat_b=1 ..."

    Wenn es mal mehr werden, hast du hier ein Problem. Du hast eigentlich jetzt auch schon eines, denn so eine Query zu verfassen ist nichts worauf man stolz sein sollte ;-)

    Da ich aber gelesen habe, dass es nicht selten vorkommt, dass Tabellen aus 100 oder mehr Spalten bestehen

    Also bei mir kam das noch nie vor und ich würde in diesem Fall (der sich noch nie ergeben hat) alles tun um das zu vermeiden.