Effiziente Abfrage für ein erweitertes EAV-Modell


7

Ich bin ein bisschen festgefahren, wie man eine effiziente Abfrage erstellt, die die Daten der folgenden EAV-Struktur zurückgibt.

Heute existiert bereits eine Produkttabelle mit 4 festen Feldern. Wir möchten das System aktualisieren und so unendlich viele zusätzliche Produktfelder zulassen, die vom Hersteller jedes Produkts definiert werden. Wir nennen diese zusätzlichen Felder "Parameter".

Ein Parameter kann vom folgenden Datentyp sein:

  • Text
  • Datumsbereich)
  • Boolescher Wert
  • Dropdown-Wert (einzeln)
  • Dropdown-Werte (mehrfach)

Darauf aufbauend habe ich folgendes Datenbankmodell erstellt:

Datenbankschema ::

  • Produkt : Die ursprüngliche Produkttabelle mit den festen Feldern.
  • Parameter : Diese Tabelle definiert alle Arten von Parametern. Derzeit 5: Text, Datum, Boolescher Wert, Dropdown-Wert (einzeln), Dropdown-Wert (mehrfach)
  • Herstellerparameter : In dieser Tabelle wird gespeichert, welche Parameter vom Produkthersteller (Herstellertabelle nicht im Bild enthalten) für alle seine Produkte definiert werden.
  • ProductParameter : Hier werden die tatsächlichen Produktdaten aller definierten Parameter gespeichert. Ein Parameter vom Typ 'Text' speichert seine Informationen in den Feldern 'Text', 'Datum' in 'DatumBegin' und 'DatumEnd', Bit / Boolean in 'Boolean'.
  • ParameterValueListItem : Diese Tabelle enthält die vordefinierten Werte der Dropdown-Listenwerte für die Einzel- und Mehrfachkopplung. Dies sind die Werte, die Sie in der Dropdown-Liste sehen, wenn Sie ein Produkt erstellen oder ändern.
  • ProductParameterValueListItem : Hier werden die tatsächlichen Produktdaten für die Dropdown-Listenwerte gespeichert.

Ich muss eine Webseite in ASP.NET C # erstellen, die eine Liste aller Produktdaten einschließlich aller definierten Parameter mit ihrem Wert für dieses Produkt anzeigt. Ich muss in der Lage sein, die Liste zu filtern, indem ich nach einem beliebigen Feld / Parameter suche. Diese Liste muss auch für jedes Feld / jeden Parameter sortierbar sein.

Ich habe versucht, die Liste durch äußere Verknüpfung von Produkt - ProductParameter - ProductParametervalueListItem - ParameterValueListItem zu filtern, um ein Tabellenergebnis mit allen Daten zu erhalten, und diese Liste dann mithilfe einer dynamischen where-Klausel zu filtern, die von der Web-App erstellt und übergeben wurde. Dies funktioniert, wenn Sie nach einem Parameter suchen. Wenn Sie jedoch nach mehreren Parametern suchen, erhalten Sie falsche Ergebnisse, da jeder Parameter eine andere Zeile im Tabellenergebnis ist und es einfach keine UND-Übereinstimmung zwischen Parameter A und B gibt, da diese existieren nicht im selben Datensatz (Zeile).

Kann mir jemand raten, wie dies erreicht werden kann? Die perfekteste Lösung wäre 1 Tabellenergebnis, das alle Daten und alle Parameter enthält, die als Spalte in derselben Produktzeile angezeigt werden. Ist dies zu komplex, um auf Datenbankebene behandelt zu werden?

Danke, dass Sie so weit gelesen haben. Jeder Rat ist willkommen.

Antworten:


3

Zuallererst ist das, was Sie entwerfen möchten, wahrscheinlich eine SEHR schlechte Idee. Eine viel bessere Lösung wäre ein dynamisches Schema, in dem Sie neue Tabellen hinzufügen und die Anwendung verstehen lässt, wie diese Tabellen abgefragt werden (Sie können sie in ein Schema einfügen). Dies vermeidet weitgehend alle Probleme mit dem Sperr- und Abfrageplan, auf die Sie bei diesem Modell stoßen müssen. An ab CREATE TABLEund zu ausgeführten Anwendungen ist nichts auszusetzen .

Zweitens bin ich mir nicht sicher, warum Sie sich Parameterin eine eigene Tabelle normalisiert haben . Warum nicht direkt auf den ManufacturerParameterTisch legen ?

Drittens, wenn Sie darauf bestehen, mit Ihrem aktuellen Modell fortzufahren, gibt es Möglichkeiten, das zu erreichen, was Sie wollen (zumindest, wenn ich Ihre Anforderung richtig interpretiere). Was Sie tun können, ist, Ihre Abfrage so zu schreiben, dass sie das Suchargument zusammenfasst, wenn eine Übereinstimmung vorliegt, und dann HAVINGdie übereinstimmenden Werte herauszufiltern. Ich gehe davon aus, dass nur eines der Felder Text, Boolean, Datumetc sind pro bevölkerten ProductParameterRekord (Sie wahrscheinlich dies mit einer Einschränkung erzwingen wollen)

Um beispielsweise nach allen Produkten zu suchen, die für einen Parameter einen bolean = true und für einen anderen Parameter einen text = 'abc' haben, können Sie Folgendes tun:

SELECT P.Name
FROM Product P
JOIN ProductParameter PP
WHERE P.ID = Foo
  AND PP.Boolean = 1 OR PP.Text = 'abc'  ... /* For each filter */
GROUP BY P.Name /* And any other things you want out of product */
HAVING COUNT(*) >= [Number of where clauses]

Wenn Sie alle Parameter dieses Produkts auflisten müssen, können Sie die obige Abfragevorlage als verschachtelte Abfrage verwenden und sich wieder anschließen ProductParameter.

Die obige Abfrage kann optimiert werden, indem eine berechnete Spalte verwaltet wird ProductParameter, die eine Zeichenfolgendarstellung der verschiedenen Datentypen in dieser Tabelle enthält. Auf diese Weise können die obigen OR-Anweisungen als IN-Liste umgeschrieben werden (die Sie als Tabellenwertparameter übergeben möchten).

Ich möchte wiederholen, dass das, was Sie tun, wahrscheinlich sehr falsch ist. Wenn Sie dies tun, müssen Sie höchstwahrscheinlich die meisten Ihrer Abfragepläne von Hand optimieren - der Optimierer hilft Ihnen nicht mehr. Und das setzt voraus, dass Sie nicht zu viele Abfragevarianten haben, die Ihren Plan-Cache voll ausführen.


1

Kann mir jemand raten, wie dies erreicht werden kann? Die perfekteste Lösung wäre 1 Tabellenergebnis, das alle Daten und alle Parameter enthält, die als Spalte in derselben Produktzeile angezeigt werden. Ist dies zu komplex, um auf Datenbankebene behandelt zu werden?

Sie können dynamisches SQL verwenden, um den EAV in eine Tabelle mit vielen Spalten zu schwenken (beachten Sie das Spaltenlimit von 1024) und jedes Produkt in einer Zeile zu haben. Ich denke nicht, dass dies so gut funktionieren würde wie die traditionellere, eng indizierte Tabelle, aber Sie könnten versuchen, die Ergebnismenge in einer Tabelle zu materialisieren und darauf Indizes zu setzen.


Ich habe vorher in den Pivot-Befehl geschaut, konnte aber nicht herausfinden, wie das geht, da der Wert eines Parameters (zusätzliches Feld) in 3 verschiedenen Feldern und sogar in anderen Tabellen gespeichert werden kann, je nachdem, welcher Datentyp ausgewählt wurde ( Text, Datum, Boolescher Wert, DDLValues ​​(einzeln), DDLValues ​​(mehrfach). Wissen Sie, ob dies mit einer
solchen

1
Sie können ein XML-Schema erstellen und die Wertestruktur in einer XMLValue-Spalte speichern.
g2server

Ich habe ein EAV-System, in dem wir dynamisch PIVOT-Ansichten erstellen, aber wie @ g2server vorschlägt. Sobald wir über ein paar Millionen Zeilen in der "Wert" -Tabelle waren, lief der Lauf schrecklich langsam. Sie können eine Ansicht mit PIVOT nicht direkt indizieren, also SELECT .. INTO ist möglicherweise Ihre beste Hoffnung.
Michael Green

@MichaelGreen Ich würde gerne etwas näher darauf eingehen. Also SELECT..INTO hat die Abfragezeit verkürzt?
user3308043

@ user3308043 - Nein, das Ausführen einer PIVOT-Abfrage in einem EAV-Schema mit Hunderten von Attributen und mehreren Millionen Zeilen hat die Abfragezeiten verkürzt. Die Lösung bestand darin, diese PIVOT-Abfrage außerhalb der Spitzenzeiten in eine "normalisierte" Tabelle AUSZUWÄHLEN und diese "normalisierte" Tabelle dann während der Geschäftszeiten abzufragen.
Michael Green
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.