On 2013-02-27, Thomas 'PointedEars' Lahn wrote:
> Bernd Nawothnig wrote:
>
>> On 2013-02-21, Claus Reibenstein wrote:
>>>>>> Dafür gibt es M:N Beziehungen.
>>>>> Nein, dafür gerade nicht.
>>>> Aber genau dafür gibt es sie.
>>> Du kennst den Datentyp SET? Damit lassen sich bis zu 64 Attribute
>>> verwalten, ohne dass dazu zwei separate Tabellen notwendig wären.
>>
>> Und welche Indextypen unterstützt SET?
>
> RTFM. SET ist ein String-Typ, bei dem die Werte als Zahlen gespeichert
> werden. Somit dürften damit nicht nur alle Indextypen möglich sein, die für
> String-Typen möglich sind, sondern sogar alle Indextypen, welche die
> jeweilige DB-Engine unterstützt.
Die nützen nur nichts, wenn Du auf einzelne Bits zugreifen willst, was
man hier aber braucht. Guckst Du etwa hier:
http://forums.mysql.com/read.php?115,125965,126209#msg-126209
Index is hardly usable with SET datatype.
The function FIND_IN_SET() will make the index unusable.
Oder hier:
http://ftp.nchu.edu.tw/MySQL/tech-resources/articles/mysql-set-datatype.html
an INDEX on a set datatype is going to refer to the set as a whole and
^^^
will not be used for searching individual elements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Also genau wie ich bereits vermutete: so ein Index ist wertlos.
>> Kann man damit _effizient_ z.B. bei 1 Million Produkten nur die
>> herausfischen, die die Eigenschaften 1, 5 und 8 haben?
>
> Ja. Mit SET('Y', 'N'):
>
> SELECT `products_id`
> FROM `products`
> WHERE `property1` = 'Y' AND `property5` = 'Y' AND `property8` = 'Y'
So kommst Du an die Werte ran, das hatte niemand bestritten. Aber
effizient ist das mitnichten. Hier greift nämlich kein Index (s.o),
auch wenn er existieren mag.
> oder mit SET('p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8'):
>
> SELECT `products_id`
> FROM `products`
> WHERE FIND_IN_SET('p1', `properties`) > 0
> AND FIND_IN_SET('p5', `properties`) > 0
> AND FIND_IN_SET('p8', `properties`) > 0
>
> bzw.
>
> SELECT `products_id`
> FROM `products`
> WHERE `properties` LIKE '%p1%'
> AND `properties` LIKE '%p5%'
> AND `properties` LIKE '%p8%'
>
> wobei bei letzterer Variante auch Infixe berücksichtigt werden (das kann
> sowohl positiv als auch negativ sein).
>
> ('p1', 'p2' usw. sind hier durch aussagekräftige Werte zu ersetzen, wie
> 'geschliffen', 'gestrahlt' usw.; die Verwendung englischsprachiger Worte ist
> dann empfehlenswert.)
Alles hart kodiert. Was machst Du, wenn Lokalisierung gewünscht ist?
Geht nicht? Sehr professionell, wirklich. Ich bin beeindruckt.
> Am effizientesten bei der Abfrage ist hier aber wohl der String-Typ
> ENUM('Y', 'N') mit mehreren Feldern, wobei der Index wieder eine Zahl ist.
> MySQL verwendet intern (für die in `mysql`.`user` gespeicherten
> Berechtigungen) jenen Datentyp so. Die Abfrage ist dann dieselbe wie für
> SET('Y', 'N').
Wie so ein Index funktioniert, weißt Du anscheinend nicht. Warum nur
wundert mich das nicht mehr?
Wenn das Indizieren komplexer Datentypen so einfach wäre, wie
Spitzohren sich das denken, warum gibt es dann wohl sowas wie GIN oder
GIST, hm?
>> Mit einer many to many Beziehung geht das, und zwar effizient.
>
> Es ist mit Deinem Ansatz eine vergleichsweise komplizierte,
> notwendigerweise komplexere und notwendigerweise ineffizientere
> asemantische Abfrage mit JOINs nötig, um die Datensätze für Objekte zu
> liefern, die gleiche Eigenschaften haben, nämlich (optimiert):
Erstmal geht es um das Filtern, also das Anwenden etwaiger WHERE
Klauseln. Und die sind beim sauberen Ansatz um Größenordnungen(!)
schneller, nämlich O(n) vs. O(log n) bzw. auch O(1), wenn man, was
hier möglich wäre, einen Hashindex wählt.
Und wenn die Datenbank Index-only-Scans unterstützt, kann das pure
Herauslesen der Information noch mal um den Faktor 10-20 mal schneller
sein. Dann wird nämlich nur noch der Index angefasst, falls alle
gewünschten Spalten indiziert sind.
> SELECT `p`.`products_id`
> FROM `products` AS `p`
> LEFT JOIN `properties` AS `prop` USING (`products_id`)
> WHERE `prop`.`value` IN (1, 5, 8)
>
> (Soll die Abfrage semantisch sein, wird es *noch* ineffizienter, weil ein
> LEFT JOIN mit der Eigenschafts-ID-zu-Eigenschaftsname-Tabelle hinzukommt.)
Hängt von der Größe des Ergebnissets ab. Nebenbei enthält Dein SET
ebenfalls keine Beschreibungen.
> Dieser Ansatz skaliert _nicht_ gut, der mit SET(…) oder einem Feld je
> Eigenschaft jedoch schon.
Der SET Ansatz führt zum Tablescan beim Suchen, also worst case, weil
nun mal auf einzelne Bits normale Indizes nicht anwendbar sind (s.o.).
Schlechter geht es also kaum noch. Der many to many Ansatz hingegen
ist perfekt indizierbar, also so indizierbar, dass der Index beim
Suchen auch sinnvoll verwendet werden kann.
> Denn es werden in der Regel mehr Datensätze als Felder sein.
>
> Der/Dein Ansatz mit Primär- und Fremdschlüsseln lohnt sich dann, wenn
> weitere Eigenschaften gespeichert werden sollen.
Was man im Normalfall immer ermöglichen sollte. Das ist eben der
Unterschied zwischen richtigen und Hobbylösungen.
Der Hobbyfrickler kümmert sich nicht um den u.U. erheblichen Aufwand,
den es kosten kann, wenn man ein zu starres und unflexibles Design im
nachhinein (und das ist immer die teuerste aller Möglichkeiten)
ändern muss. Der tanzt lieber frenetisch um jedes Bit, was er meint,
gespart zu haben, ohne nach links oder rechts zu gucken. Dass gerade
Speicherplatz heute der geringste aller Kostenfaktoren ist,
interessiert ihn nicht groß.
> Aus diesem Grund werden bei vom Benutzer erweiter- und anpassbaren
> Shopsystemen wie Magento (MySQL- basiert) aus der komplexen, Primär-
> und Fremdschlüssel-basierten grundlegenden Datenbankstruktur für die
> häufige Benutzung, wie zum Beispiel für eine Volltextsuche, sogenannte
> “flat tables” erstellt, welche die betreffenden Eigenschaften als
> Werte von atomaren Eigenschaftsfeldern bereitstellen.
Genau, atomar ist das Schlüsselwort bei der ersten Normalform. Wer das
nicht beherzigt, hat noch nicht mal die Basics der relationalen
Datenbanken verstanden.
> Die Varianten mit SET und mit mehreren Feldern (INT, ENUM, etc.) haben
> gegenüber der Primär-Fremdschlüssel-Variante auch einen Nachteil: Soll eine
> Eigenschaft hinzugefügt werden und hat die Tabelle bereits viele Datensätze,
> kann das Hinzufügen länger dauern, weil dafür die Tabellenstruktur geändert
> werden muss. Neben der Anzahl Datensätze ist daher zur Wahl des Ansatzes
> auch zu bedenken, wie wahrscheinlich solche Änderungen sind.
Und bei bereits 64 Eigenschaften ist Schluss - alleine sowas
disqualifiert den Ansatz bereits, denn eine saubere many to many
Beziehung, die da vollkommen flexibel und offen ist, ist kaum
aufwendiger zu implementieren.
>> Klar kostet die mehr Speicher als Hobbylösungen, wie man sie im
>> MySQL-Umfeld leider häufiger antrifft. Aber sie skaliert eben deutlich
>> besser, was freilich erst bei großen Datenmengen auffällt. Allerdings
>> gilt auch: wenn die Datenmenge eh klein ist, stört auch die
>> zusätzliche Tabelle nicht.
>>
>> Meine Erfahrung mit großen Datenmengen lautet: es ist immer verkehrt,
>> wegen ein paar Byte gegen gute und richtige Ansätze zu verstoßen, auch
>> wenn man erstmal mehr Schreibarbeit damit hat. Dieses bisschen mehr
>> Aufwand lohnt sich eigentlich immer.
>
> Dein Ansatz ist aber *in diesem Fall* anscheinend (der OP liefert keinen
> Kontext) _nicht_ der richtige, denn es ist *in diesem Fall* anscheinend
> _nicht_ der beste. Aber wenn man so wie Du nur einen Hammer hat, sieht eben
> jedes Problem wie ein Nagel aus.
>
> kopfschüttelnd,
Für Nägel sollte man sinnvollerweise schon einen Hammer und nicht
gänzlich oder weitgehend ungeeignete Werkzeuge verwenden.
Bernd