Meine Anforderungen sind:
- Muss in der Lage sein, benutzerdefinierte Felder eines beliebigen Datentyps dynamisch hinzuzufügen
- Muss in der Lage sein, UDFs schnell abzufragen
- Sie müssen in der Lage sein, Berechnungen für UDFs basierend auf dem Datentyp durchzuführen
- Muss in der Lage sein, UDFs nach Datentyp zu sortieren
Andere Informationen:
- Ich suche hauptsächlich Leistung
- Es gibt einige Millionen Stammsätze, an die UDF-Daten angehängt werden können
- Bei meiner letzten Überprüfung befanden sich in unserer aktuellen Datenbank über 50 Mio. UDF-Datensätze
- Meistens ist eine UDF nur an einige Tausend der Stammdatensätze angehängt, nicht an alle
- UDFs werden nicht verbunden oder als Schlüssel verwendet. Es handelt sich lediglich um Daten, die für Abfragen oder Berichte verwendet werden
Optionen:
Erstellen Sie eine große Tabelle mit StringValue1, StringValue2 ... IntValue1, IntValue2 usw. Ich hasse diese Idee, werde sie aber in Betracht ziehen, wenn mir jemand sagen kann, dass sie besser ist als andere Ideen und warum.
Erstellen Sie eine dynamische Tabelle, die bei Bedarf eine neue Spalte hinzufügt. Diese Idee gefällt mir auch nicht, da ich der Meinung bin, dass die Leistung langsam ist, wenn Sie nicht jede Spalte indizieren.
Erstellen Sie eine einzelne Tabelle mit UDFName, UDFDataType und Value. Wenn eine neue UDF hinzugefügt wird, generieren Sie eine Ansicht, die genau diese Daten abruft und sie in den angegebenen Typ analysiert. Elemente, die die Analysekriterien nicht erfüllen, geben NULL zurück.
Erstellen Sie mehrere UDF-Tabellen, eine pro Datentyp. Wir hätten also Tabellen für UDFStrings, UDFDates usw. Wahrscheinlich würden wir das Gleiche wie # 2 tun und automatisch eine Ansicht generieren, wenn ein neues Feld hinzugefügt wird
XML-Datentypen? Ich habe noch nie damit gearbeitet, aber ich habe gesehen, dass sie erwähnt wurden. Ich bin mir nicht sicher, ob sie mir die gewünschten Ergebnisse liefern würden, insbesondere bei der Leistung.
Etwas anderes?
Antworten:
Wenn Leistung das Hauptanliegen ist, würde ich mit # 6 ... eine Tabelle pro UDF gehen (wirklich, dies ist eine Variante von # 2). Diese Antwort ist speziell auf diese Situation und die Beschreibung der beschriebenen Datenverteilungs- und Zugriffsmuster zugeschnitten.
Vorteile:
Da Sie angeben, dass einige UDFs Werte für einen kleinen Teil des Gesamtdatensatzes haben, bietet eine separate Tabelle die beste Leistung, da diese Tabelle nur so groß ist, wie sie zur Unterstützung der UDF erforderlich ist. Gleiches gilt für die zugehörigen Indizes.
Sie erhalten auch einen Geschwindigkeitsschub, indem Sie die Datenmenge begrenzen, die für Aggregationen oder andere Transformationen verarbeitet werden muss. Durch Aufteilen der Daten in mehrere Tabellen können Sie einige der aggregierenden und anderen statistischen Analysen der UDF-Daten durchführen und dieses Ergebnis dann über einen Fremdschlüssel mit der Mastertabelle verknüpfen, um die nicht aggregierten Attribute zu erhalten.
Sie können Tabellen- / Spaltennamen verwenden, die die tatsächlichen Daten widerspiegeln.
Sie haben die vollständige Kontrolle über die Verwendung von Datentypen, das Überprüfen von Einschränkungen, Standardwerten usw. zum Definieren der Datendomänen. Unterschätzen Sie nicht den Leistungseinbruch, der sich aus der direkten Datentypkonvertierung ergibt. Solche Einschränkungen helfen auch RDBMS-Abfrageoptimierern, effektivere Pläne zu entwickeln.
Sollten Sie jemals Fremdschlüssel verwenden müssen, wird die integrierte deklarative referenzielle Integrität durch die Durchsetzung von Einschränkungen auf Trigger- oder Anwendungsebene nur selten übertroffen.
Nachteile:
Dies könnte viele Tabellen erstellen. Das Erzwingen einer Schematrennung und / oder einer Namenskonvention würde dies erleichtern.
Für den Betrieb der UDF-Definition und -Verwaltung ist mehr Anwendungscode erforderlich. Ich gehe davon aus, dass immer noch weniger Code benötigt wird als für die ursprünglichen Optionen 1, 3 und 4.
Weitere Überlegungen:
Wenn es irgendetwas an der Art der Daten gibt, das für die Gruppierung der UDFs sinnvoll wäre, sollte dies gefördert werden. Auf diese Weise können diese Datenelemente zu einer einzigen Tabelle zusammengefasst werden. Angenommen, Sie haben UDFs für Farbe, Größe und Kosten. Die Tendenz in den Daten ist, dass die meisten Instanzen dieser Daten so aussehen
eher, als
In einem solchen Fall entsteht keine merkliche Geschwindigkeitsstrafe, wenn Sie die 3 Spalten in einer Tabelle kombinieren, da nur wenige Werte NULL sind und Sie vermeiden, 2 weitere Tabellen zu erstellen. Dies sind 2 weniger Verknüpfungen erforderlich, wenn Sie auf alle 3 Spalten zugreifen müssen .
Wenn Sie von einer UDF, die stark bevölkert und häufig verwendet wird, auf eine Leistungswand stoßen, sollte dies für die Aufnahme in die Mastertabelle berücksichtigt werden.
Das logische Tabellendesign kann Sie zu einem bestimmten Punkt führen. Wenn die Anzahl der Datensätze jedoch sehr hoch wird, sollten Sie sich auch ansehen, welche Optionen für die Tabellenpartitionierung von Ihrem RDBMS Ihrer Wahl bereitgestellt werden.
quelle
Ich habe viel über dieses Problem geschrieben . Die gebräuchlichste Lösung ist das Entity-Attribute-Value-Antimuster, das dem ähnelt, was Sie in Ihrer Option 3 beschreiben. Vermeiden Sie dieses Design wie die Pest .
Was ich für diese Lösung verwende, wenn ich wirklich dynamische benutzerdefinierte Felder benötige, ist, sie in einem XML-Blob zu speichern, damit ich jederzeit neue Felder hinzufügen kann. Um dies zu beschleunigen, erstellen Sie auch zusätzliche Tabellen für jedes Feld, das Sie durchsuchen oder sortieren müssen (Sie haben keine Tabelle pro Feld - nur eine Tabelle pro durchsuchbarem Feld). Dies wird manchmal als invertiertes Indexdesign bezeichnet.
Einen interessanten Artikel aus dem Jahr 2009 über diese Lösung finden Sie hier: http://backchannel.org/blog/friendfeed-schemaless-mysql
Sie können auch eine dokumentorientierte Datenbank verwenden, in der benutzerdefinierte Felder pro Dokument erwartet werden. Ich würde Solr wählen .
quelle
fieldname
odertablename
das Speichern von Metadaten- IDs als Datenzeichenfolgen ist der Beginn vieler Probleme. Siehe auch en.wikipedia.org/wiki/Inner-platform_effectIch würde höchstwahrscheinlich eine Tabelle mit folgender Struktur erstellen:
Die genauen Arten von Kursen hängen von Ihren Anforderungen ab (und natürlich von den von Ihnen verwendeten DBMS). Sie können auch das NumberValue-Feld (dezimal) für Ints und Boolesche Werte verwenden. Möglicherweise benötigen Sie auch andere Typen.
Sie benötigen einen Link zu den Stammdatensätzen, denen der Wert gehört. Es ist wahrscheinlich am einfachsten und schnellsten, für jede Mastertabelle eine Benutzerfeldtabelle zu erstellen und einen einfachen Fremdschlüssel hinzuzufügen. Auf diese Weise können Sie Stammsätze einfach und schnell nach Benutzerfeldern filtern.
Möglicherweise möchten Sie eine Art von Metadateninformationen haben. Am Ende haben Sie also Folgendes:
Tabelle UdfMetaData
Tabelle MasterUdfValues
Was immer Sie tun, würde ich nicht die Tabellenstruktur ändern sich dynamisch. Es ist ein Alptraum für die Instandhaltung. Ich würde auch keine XML-Strukturen verwenden, sie sind viel zu langsam.
quelle
Dies klingt nach einem Problem, das möglicherweise besser durch eine nicht relationale Lösung wie MongoDB oder CouchDB gelöst werden kann.
Beide ermöglichen eine dynamische Schemaerweiterung, während Sie gleichzeitig die gewünschte Tupelintegrität beibehalten können.
Ich stimme Bill Karwin zu, das EAV-Modell ist für Sie kein performanter Ansatz. Die Verwendung von Name-Wert-Paaren in einem relationalen System ist an sich nicht schlecht, funktioniert jedoch nur dann gut, wenn das Name-Wert-Paar ein vollständiges Tupel an Informationen enthält. Wenn Sie es verwenden, um eine Tabelle zur Laufzeit dynamisch zu rekonstruieren, werden alle möglichen Dinge schwierig. Das Abfragen wird zu einer Übung in der Pivot-Wartung oder zwingt Sie, die Tupelrekonstruktion in die Objektebene nach oben zu verschieben.
Sie können nicht feststellen, ob ein Nullwert oder ein fehlender Wert ein gültiger Eintrag oder ein fehlender Eintrag ist, ohne Schema-Regeln in Ihre Objektebene einzubetten.
Sie verlieren die Fähigkeit, Ihr Schema effizient zu verwalten. Ist ein 100-stelliger Varchar der richtige Typ für das Feld "Wert"? 200 Zeichen? Sollte es stattdessen nvarchar sein? Es kann ein harter Kompromiss sein, der dazu führt, dass Sie der Dynamik Ihres Sets künstliche Grenzen setzen müssen. So etwas wie "Sie können nur x benutzerdefinierte Felder haben und jedes kann nur y Zeichen lang sein.
Mit einer dokumentenorientierten Lösung wie MongoDB oder CouchDB verwalten Sie alle einem Benutzer zugeordneten Attribute in einem einzigen Tupel. Da Joins kein Thema sind, ist das Leben glücklich, da keiner dieser beiden trotz des Hype gut mit Joins zurechtkommt. Ihre Benutzer können so viele Attribute definieren, wie sie möchten (oder Sie werden es zulassen), und zwar in Längen, die erst schwer zu verwalten sind, wenn Sie ungefähr 4 MB erreichen.
Wenn Sie Daten haben, für die Integrität auf ACID-Ebene erforderlich ist, können Sie die Lösung aufteilen, wobei die Daten mit hoher Integrität in Ihrer relationalen Datenbank und die dynamischen Daten in einem nicht relationalen Speicher gespeichert sind.
quelle
Selbst wenn Sie einem Benutzer die Möglichkeit geben, benutzerdefinierte Spalten hinzuzufügen, ist es nicht unbedingt so, dass die Abfrage dieser Spalten eine gute Leistung erbringt. Es gibt viele Aspekte, die in das Abfragedesign einfließen und eine gute Leistung ermöglichen. Der wichtigste davon ist die richtige Angabe, was überhaupt gespeichert werden soll. Wollen Sie den Benutzern grundsätzlich ermöglichen, ein Schema ohne Rücksicht auf Spezifikationen zu erstellen und schnell Informationen aus diesem Schema abzuleiten? Wenn ja, ist es unwahrscheinlich, dass eine solche Lösung gut skaliert werden kann, insbesondere wenn Sie dem Benutzer erlauben möchten, numerische Analysen der Daten durchzuführen.
Option 1
IMO dieser Ansatz gibt Ihnen Schema ohne Wissen darüber, was das Schema bedeutet, was ein Rezept für eine Katastrophe und ein Albtraum für Berichtsdesigner ist. Das heißt, Sie müssen über die Metadaten verfügen, um zu wissen, in welcher Spalte welche Daten gespeichert sind. Wenn diese Metadaten durcheinander geraten, können Ihre Daten möglicherweise verloren gehen. Außerdem ist es einfach, die falschen Daten in die falsche Spalte zu setzen. ("Was? String1 enthält den Namen von Klöstern? Ich dachte, es wäre Chalie Sheens Lieblingsdroge.")
Option 3,4,5
IMO, Anforderungen 2, 3 und 4 eliminieren jede Variation eines EAV. Wenn Sie diese Daten abfragen, sortieren oder berechnen müssen, ist ein EAV der Traum von Cthulhu und der Albtraum Ihres Entwicklungsteams und Ihres Datenbankadministrators. EAVs verursachen einen Leistungsengpass und bieten Ihnen nicht die Datenintegrität, die Sie benötigen, um schnell zu den gewünschten Informationen zu gelangen. Abfragen werden schnell zu gordischen Kreuztabellenknoten.
Option 2,6
Das lässt wirklich eine Wahl: Sammeln Sie Spezifikationen und bauen Sie dann das Schema aus.
Wenn der Kunde die beste Leistung für Daten erzielen möchte, die er speichern möchte, muss er mit einem Entwickler zusammenarbeiten, um seine Anforderungen zu verstehen und diese so effizient wie möglich zu speichern. Es kann weiterhin in einer vom Rest der Tabellen getrennten Tabelle mit Code gespeichert werden, der dynamisch ein Formular basierend auf dem Schema der Tabelle erstellt. Wenn Sie über eine Datenbank verfügen, die erweiterte Eigenschaften für Spalten zulässt, können Sie diese sogar verwenden, um dem Formularersteller dabei zu helfen, schöne Beschriftungen, QuickInfos usw. zu verwenden, sodass Sie lediglich das Schema hinzufügen müssen. In jedem Fall müssen die Daten ordnungsgemäß gespeichert werden, damit Berichte effizient erstellt und ausgeführt werden können. Wenn die fraglichen Daten viele Nullen haben, können einige Datenbanken diese Art von Informationen speichern. Beispielsweise,
Wenn dies nur eine Tüte Daten wäre, für die keine Analyse, Filterung oder Sortierung durchgeführt werden sollte, würde ich sagen, dass eine Variation eines EAV den Trick machen könnte. Angesichts Ihrer Anforderungen besteht die effizienteste Lösung darin, die richtigen Spezifikationen zu erhalten, selbst wenn Sie diese neuen Spalten in separaten Tabellen speichern und Formulare dynamisch aus diesen Tabellen erstellen.
Sparse Columns
quelle
Nach meinen Recherchen helfen Ihnen mehrere Tabellen basierend auf dem Datentyp nicht bei der Leistung. Besonders wenn Sie Massendaten haben, wie 20K- oder 25K-Datensätze mit mehr als 50 UDFs. Die Leistung war am schlechtesten.
Sie sollten eine einzelne Tabelle mit mehreren Spalten verwenden, z.
quelle
Dies ist eine problematische Situation, und keine der Lösungen erscheint "richtig". Option 1 ist jedoch wahrscheinlich sowohl in Bezug auf die Einfachheit als auch in Bezug auf die Leistung die beste.
Dies ist auch die Lösung, die in einigen kommerziellen Unternehmensanwendungen verwendet wird.
BEARBEITEN
Eine andere Option, die jetzt verfügbar ist, aber nicht vorhanden war (oder zumindest nicht ausgereift war), als die Frage ursprünglich gestellt wurde, ist die Verwendung von JSON-Feldern in der Datenbank.
Viele relationale DBs unterstützen jetzt JSON-basierte Felder (die eine dynamische Liste von Unterfeldern enthalten können) und ermöglichen das Abfragen dieser Felder
Postgress
MySQL
quelle
Ich habe Erfahrung oder 1, 3 und 4 und sie alle enden entweder chaotisch, wobei nicht klar ist, was die Daten sind, oder wirklich kompliziert mit einer weichen Kategorisierung, um die Daten in dynamische Arten von Datensätzen aufzuteilen.
Ich wäre versucht, XML auszuprobieren. Sie sollten in der Lage sein, Schemas gegen den Inhalt der XML zu erzwingen, um die Datentypisierung usw. zu überprüfen. Dies hilft dabei, unterschiedliche Sätze von UDF-Daten zu speichern. In neueren Versionen von SQL Server können Sie XML-Felder indizieren, was die Leistung verbessern soll. (siehe http://blogs.technet.com/b/josebda/archive/2009/03/23/sql-server-2008-xml-indexing.aspx ) zum Beispiel
quelle
Wenn Sie SQL Server verwenden, übersehen Sie nicht den SQL-Variantentyp. Es ist ziemlich schnell und sollte Ihren Job machen. Andere Datenbanken haben möglicherweise etwas Ähnliches.
XML-Datentypen sind aus Leistungsgründen nicht so gut. Wenn Sie Berechnungen auf dem Server durchführen, müssen Sie diese ständig deserialisieren.
Option 1 klingt schlecht und sieht grob aus, aber in Bezug auf die Leistung kann dies die beste Wahl sein. Ich habe bereits Tabellen mit Spalten mit dem Namen Field00-Field99 erstellt, da Sie die Leistung einfach nicht übertreffen können. Möglicherweise müssen Sie auch Ihre INSERT-Leistung berücksichtigen. In diesem Fall ist dies auch die richtige Wahl. Sie können jederzeit Ansichten für diese Tabelle erstellen, wenn Sie möchten, dass sie ordentlich aussieht!
quelle
SharePoint verwendet Option 1 und weist eine angemessene Leistung auf.
quelle
Ich habe dies in der Vergangenheit sehr erfolgreich mit keiner dieser Optionen geschafft (Option 6? :)).
Ich erstelle ein Modell, mit dem die Benutzer spielen können (als XML speichern und über ein benutzerdefiniertes Modellierungswerkzeug verfügbar machen) und aus dem Modell generierte Tabellen und Ansichten, um die Basistabellen mit den benutzerdefinierten Datentabellen zu verbinden. Jeder Typ hätte also eine Basistabelle mit Kerndaten und eine Benutzertabelle mit benutzerdefinierten Feldern.
Nehmen Sie als Beispiel ein Dokument: Typische Felder sind Name, Typ, Datum, Autor usw. Dies wird in die Kerntabelle aufgenommen. Dann würden Benutzer ihre eigenen speziellen Dokumenttypen mit ihren eigenen Feldern definieren, z. B. contract_end_date, erneueral_clause, bla bla bla. Für dieses benutzerdefinierte Dokument gibt es die Kerndokumenttabelle, die xcontract-Tabelle, die mit einem gemeinsamen Primärschlüssel verknüpft ist (sodass der xcontracts-Primärschlüssel auch für den Primärschlüssel der Kerntabelle fremd ist). Dann würde ich eine Ansicht generieren, um diese beiden Tabellen zu verpacken. Die Leistung beim Abfragen war schnell. Zusätzliche Geschäftsregeln können ebenfalls in die Ansichten eingebettet werden. Das hat bei mir sehr gut funktioniert.
quelle
Unsere Datenbank unterstützt eine SaaS-App (Helpdesk-Software), in der Benutzer über 7.000 "benutzerdefinierte Felder" haben. Wir verwenden einen kombinierten Ansatz:
(EntityID, FieldID, Value)
Tabelle zum Durchsuchen der Datenentities
Tabelle, das alle Entitätswerte enthält und zur Anzeige der Daten verwendet wird. (Auf diese Weise benötigen Sie nicht eine Million JOINs, um die Werte zu erhalten).Sie könnten # 1 weiter aufteilen, um eine "Tabelle pro Datentyp" zu erhalten, wie diese Antwort nahelegt. Auf diese Weise können Sie sogar Ihre UDFs indizieren.
PS Ein paar Worte, um den "Entity-Attribute-Value" -Ansatz zu verteidigen, den jeder immer wieder verprügelt. Wir haben # 1 ohne # 2 seit Jahrzehnten verwendet und es hat gut funktioniert. Manchmal ist es eine Geschäftsentscheidung. Haben Sie Zeit, Ihre App neu zu schreiben und die Datenbank neu zu gestalten, oder können Sie ein paar Dollar auf Cloud-Server werfen, die heutzutage wirklich billig sind? Übrigens, als wir den Ansatz Nr. 1 verwendeten, enthielt unsere Datenbank Millionen von Entitäten, auf die Hunderttausende von Benutzern zugegriffen hatten, und ein 16-GB-Dual-Core-Datenbankserver lief einwandfrei
quelle
custom_fields
Tabelle, in der Werte wie 1 =>last_concert_year
, 2 =>band
, 3 => gespeichert sind ,music
und dann einecustom_fields_values
Tabelle mit den Werten 001, 1, 1976 002, 1, 1977 003, 2,Iron Maiden
003, 3 ,Metal
Hoffnung das Beispiel macht Sinn für Sie und entschuldigen uns für die Formatierung!bands
Tabelle mit einer Zeile,1,'Iron Maiden'
danncustom_fields
mit Zeilen,1,'concert_year' | 2,'music'
danncustom_fields_values
mit Zeilen1,1,'1977'|1,2,'metal'
In den Kommentaren habe ich gesehen, dass Sie gesagt haben, dass die UDF-Felder importierte Daten sichern sollen, die vom Benutzer nicht richtig zugeordnet wurden.
Möglicherweise besteht eine andere Möglichkeit darin, die Anzahl der von jedem Benutzer erstellten UDFs zu verfolgen und sie zur Wiederverwendung von Feldern zu zwingen, indem angegeben wird, dass sie 6 benutzerdefinierte Felder (oder eine andere, gleichermaßen zufällige Grenze) verwenden können.
Wenn Sie mit einem solchen Datenbankstrukturierungsproblem konfrontiert sind, ist es oft am besten, zum grundlegenden Design der Anwendung (in Ihrem Fall zum Importsystem) zurückzukehren und einige weitere Einschränkungen vorzunehmen.
Was ich jetzt tun würde, ist Option 4 (BEARBEITEN) mit dem Hinzufügen eines Links zu Benutzern:
Stellen Sie jetzt sicher, dass Sie Ansichten erstellen, um die Leistung zu optimieren und Ihre Indizes richtig zu machen. Durch diesen Normalisierungsgrad wird der DB-Footprint kleiner, Ihre Anwendung jedoch komplexer.
quelle
Ich würde # 4 empfehlen, da diese Art von System in Magento verwendet wurde , einer hoch akkreditierten E-Commerce-CMS-Plattform. Verwenden Sie eine einzelne Tabelle, um Ihre benutzerdefinierten Felder mithilfe der Spalten fieldId & label zu definieren . Dann hat getrennte Tabellen für jeden Datentyp und in jedem dieser Tabellen hat einen Index , die Indizes von fieldID und den Datentyp Wert Spalten. Verwenden Sie dann in Ihren Abfragen Folgendes:
Dies wird meiner Meinung nach die bestmögliche Leistung für benutzerdefinierte Typen gewährleisten.
Nach meiner Erfahrung habe ich an mehreren Magento-Websites gearbeitet, die monatlich Millionen von Benutzern bedienen, Tausende von Produkten mit benutzerdefinierten Produktattributen hosten und die Datenbank die Arbeitslast auch für Berichte problemlos handhaben kann.
Für die Berichterstellung können Sie die Beschriftungswerte
PIVOT
Ihrer Fields- Tabelle in Spaltennamen konvertieren und dann Ihre Abfrageergebnisse aus jeder Datentabellentabelle in diese geschwenkten Spalten schwenken.quelle