Was sind Entwurfsmuster zur Unterstützung benutzerdefinierter Felder in einer Anwendung?

72

Wir entwickeln eine kommerzielle Anwendung. Unsere Kunden bitten um Unterstützung für benutzerdefinierte Felder. Beispielsweise möchten sie dem Kundenformular ein Feld hinzufügen.

Was sind die bekannten Entwurfsmuster zum Speichern der Feldwerte und der Metadaten zu den Feldern?

Ich sehe diese Optionen jetzt:

Option 1 : Fügen Sie meiner Kundentabelle Spalten vom Typ varchar vom Typ varchar Feld1, Feld2, Feld3, Feld4 hinzu.

Option 2 : Fügen Sie der Kundentabelle eine einzelne Spalte vom Typ XML hinzu und speichern Sie die Werte der benutzerdefinierten Felder in XML.

Option 3 : Fügen Sie eine CustomerCustomFieldValue-Tabelle mit einer Spalte vom Typ varchar hinzu und speichern Sie die Werte in dieser Spalte. Diese Tabelle hätte auch eine CustomerID, eine CustomFieldID.

CustomerID,  CustomFieldID, Value
10001,       1001,          '02/12/2009 8:00 AM'
10001,       1002,          '18.26'
10002,       1001,          '01/12/2009 8:00 AM'
10002,       1002,          '50.26'

CustomFieldID wäre eine ID aus einer anderen Tabelle namens CustomField mit den folgenden Spalten: CustomFieldID, FieldName, FieldValueTypeID.

Option 4 : Fügen Sie eine CustomerCustomFieldValue-Tabelle mit einer Spalte jedes möglichen Werttyps hinzu und speichern Sie die Werte in der rechten Spalte. Ähnlich wie # 3, jedoch werden Feldwerte unter Verwendung einer stark typisierten Spalte gespeichert.

CustomerID,  CustomFieldID, DateValue,           StringValue,       NumericValue                 
10001,       1001,          02/12/2009 8:00 AM,  null,              null
10001,       1002,          null,                null,              18.26
10002,       1001,          01/12/2009 8:00 AM,  null,              null
10002,       1002,          null,                null,              50.26

Option 5 : Die Optionen 3 und 4 verwenden eine Tabelle, die für ein einzelnes Konzept (Kunde) spezifisch ist. Unsere Kunden fragen nach benutzerdefinierten Feldern auch in anderen Formen. Sollten wir stattdessen ein systemweites benutzerdefiniertes Feldspeichersystem haben? Anstatt also mehrere Tabellen wie CustomerCustomFieldValue, EmployeeCustomFieldValue, InvoiceCustomFieldValue zu haben, hätten wir eine einzige Tabelle mit dem Namen CustomFieldValue? Obwohl es mir eleganter erscheint, würde das nicht zu einem Leistungsengpass führen?

Haben Sie einen dieser Ansätze verwendet? Warst du erfolgreich? Welchen Ansatz würden Sie wählen? Kennen Sie einen anderen Ansatz, den ich in Betracht ziehen sollte?

Außerdem möchten meine Kunden, dass das benutzerdefinierte Feld auf Daten in anderen Tabellen verweisen kann. Beispielsweise möchte ein Kunde dem Kunden möglicherweise ein Feld "Bevorzugte Zahlungsmethode" hinzufügen. Zahlungsmethoden werden an anderer Stelle im System definiert. Das bringt das Thema "Fremdschlüssel" ins Bild. Sollte ich versuchen, Einschränkungen zu erstellen, um sicherzustellen, dass die in den benutzerdefinierten Feldtabellen gespeicherten Werte gültige Werte sind?

Vielen Dank

======================

EDIT 27.07.2009:

Danke für deine Antworten. Es scheint, dass die Liste der Ansätze jetzt ziemlich umfassend ist. Ich habe die Option 2 (eine einzelne XML-Spalte) ausgewählt. Es war vorerst am einfachsten zu implementieren. Ich werde wahrscheinlich auf einen stärker definierten Ansatz zurückgreifen müssen, da meine Anforderungen komplexer werden und die Anzahl der zu unterstützenden benutzerdefinierten Felder größer wird.

Sylvain
quelle
Eine Erweiterung von Option 2 ist die binäre Serialisierung anstelle von XML. Als Sie gefragt haben, welche Entwurfsmuster es gibt - Martin Fowler nennt dieses serialisierte LOB in seinem Buch Patterns of Enterprise Application Architecture - siehe martinfowler.com/eaaCatalog/serializedLOB.html
RichardOD
Ich bin gespannt auf das Ergebnis Ihres gewählten Ansatzes: Konnten Sie die benutzerdefinierten Felder mithilfe von XML (oder einem serialisierten LOB) sortieren / suchen / filtern? War dies genug für Ihre Datenspeicheranforderungen oder mussten Sie einen anderen Weg einschlagen? Es wäre großartig, wenn Sie das Wissen teilen könnten.
user1987392
Sie haben nicht beschrieben, wie der Verbraucher diese Daten verwenden wird, und dies ist ein wichtiger Faktor für die Auswahl der Designlösung.
Jangorecki
Wie hat das am Ende für Sie geklappt? Ich stehe vor dem gleichen Problem und die Informationen im Internet scheinen bestenfalls spärlich zu sein.
Joseph Devlin
1
Die einfachste Option (eine XML-Spalte in jeder Tabelle) hat sich als gut erwiesen. 7 Jahre später werden wir das Design ändern. Wir gehen mit der Änderung des Laufzeitschemas. Wenn ein Client ein Feld hinzufügt, erstellen wir im laufenden Betrieb eine neue Spalte in der Datenbank mit dem richtigen Datentyp und den richtigen Fremdschlüsseln für andere Tabellen. Die Hauptgründe für diese Änderung: Typen (Datenintegrität), Fremdschlüssel (Datenintegrität), Leistung (Indizes), Klarheit (Abfrage- und Migrationsskripte sind einfacher zu schreiben). XML war einfach zu implementieren und hat uns gute Dienste geleistet. es ist eine praktikable Option.
Sylvain

Antworten:

15

Ich stimme den unten stehenden Postern zu, dass die Optionen 3, 4 oder 5 am wahrscheinlichsten angemessen sind. Jede Ihrer vorgeschlagenen Implementierungen hat jedoch ihre Vorteile und Kosten. Ich würde vorschlagen, eine auszuwählen, indem Sie sie an Ihre spezifischen Anforderungen anpassen. Zum Beispiel:

  1. Option 1 Profis: Schnell zu implementieren. Ermöglicht DB-Aktionen für benutzerdefinierte Felder (Suchen, Sortieren).
    Option 1 Nachteile: Benutzerdefinierte Felder sind generisch, daher keine stark typisierten Felder. Die Datenbanktabelle ist in Bezug auf die Größe ineffizient, da viele externe Felder niemals verwendet werden. Die Anzahl der zulässigen benutzerdefinierten Felder muss vorweggenommen werden.
  2. Option 2 Profis: Schnell zu implementieren. Flexibel und ermöglicht eine beliebige Anzahl und Art von benutzerdefinierten Feldern.
    Option 2 Nachteile: Für benutzerdefinierte Felder sind keine DB-Aktionen möglich. Dies ist am besten, wenn Sie später nur die benutzerdefinierten Felder anzeigen oder geringfügige Änderungen an den Daten nur auf Kundenbasis vornehmen müssen.
  3. Option 3-Profis: Sowohl flexibel als auch effizient. DB-Aktionen können ausgeführt werden, aber die Daten werden etwas normalisiert, um den verschwendeten Speicherplatz zu reduzieren. Ich stimme dem Vorschlag von unknown (google) zu, eine zusätzliche Spalte hinzuzufügen, in der Typ- oder Quellinformationen angegeben werden können. Option 3 Nachteile: Leichter Anstieg der Entwicklungszeit und Komplexität Ihrer Abfragen, aber hier gibt es wirklich nicht zu viele Nachteile.
  4. Option 4 ist mit Option 3 identisch, außer dass Ihre eingegebenen Daten auf DB-Ebene bearbeitet werden können. Durch Hinzufügen von Typinformationen zur Verknüpfungstabelle in Option 3 können Sie mehr Vorgänge auf Anwendungsebene ausführen, die Datenbank kann jedoch beispielsweise keine Vergleiche oder Sortierungen durchführen. Die Wahl zwischen 3 und 4 hängt von dieser Anforderung ab.
  5. Option 5 entspricht 3 oder 4, bietet jedoch noch mehr Flexibilität, um die Lösung auf viele verschiedene Tabellen anzuwenden. Die Kosten in diesem Fall sind, dass die Größe dieser Tabelle viel größer wird. Wenn Sie viele teure Verknüpfungsvorgänge ausführen, um zu Ihren benutzerdefinierten Feldern zu gelangen, ist diese Lösung möglicherweise nicht gut skalierbar.

PS Wie unten erwähnt, bezieht sich der Begriff "Entwurfsmuster" normalerweise auf objektorientierte Programmierung. Sie suchen nach einer Lösung für ein Datenbankentwurfsproblem, was bedeutet, dass die meisten Ratschläge zu Entwurfsmustern nicht anwendbar sind.

Eric Nguyen
quelle
Ich akzeptiere diese Antwort, weil ich denke, dass sie bei der Auswahl einer Lösung hilft.
Sylvain
Zu Option 3: "(...) aber hier gibt es wirklich nicht zu viele Nachteile." → Sie versuchen nicht, Ihre Antwort glaubwürdig zu machen, aber # 3/4/5 sind so ziemlich das Entity-Value-Attribute-Muster, das von einigen als Anti-Pattern angesehen wird, und es gibt viele "Horrorgeschichten" über seine Leistung rund um das Netz. Auf der anderen Seite sagen viele, dass es möglich ist, mit XML / BLOB zu suchen / zu sortieren / zu filtern (@ serverseitig), aber ich habe es noch nie gesehen ... Ich kratzte mir am Kopf, welcher Ansatz (EAV oder XML) funktioniert am besten, besonders wenn Sie ein ORM verwenden.
user1987392
10

Was den Anwendungscode betrifft, bin ich mir nicht sicher. Ich weiß, dass benutzerdefinierte Felder stark von einem EAV-Modell in der Datenbank profitieren .

Gemäß den Kommentaren unten besteht der größte Fehler, den Sie mit diesem Modell machen können, darin, Fremdschlüssel einzugeben. Fügen Sie niemals so etwas wie FriendID oder TypeID in dieses Modell ein. Verwenden Sie dieses Modell in Verbindung mit dem typischen relationalen Modell und behalten Sie Fremdschlüsselfelder in Tabellenspalten bei, wie sie sollten.

Ein zweiter schwerwiegender Fehler ist das Platzieren von Daten in diesem Modell, die für jedes Element gemeldet werden müssen. Wenn Sie beispielsweise so etwas wie Benutzername in dieses Modell einfügen, bedeutet dies, dass Sie sich jedes Mal, wenn Sie auf einen Benutzer zugreifen möchten und dessen Benutzernamen kennen müssen, bestenfalls einem Join oder 2n Abfragen verpflichtet haben, wobei n die Anzahl der Benutzer ist, die Sie suchen . Wenn Sie bedenken, dass Sie normalerweise die Eigenschaft Benutzername für jedes Benutzerelement benötigen, wird klar, dass auch dies in den Tabellenspalten verbleiben sollte.

Wenn Sie dieses Modell jedoch nur mit benutzerdefinierten Benutzerfeldern verwenden, ist dies in Ordnung. Ich kann mir nicht viele Situationen vorstellen, in denen ein Benutzer relationale Daten eingibt und das EAV-Modell die Suche nicht allzu stark beeinträchtigt.

Versuchen Sie nicht, Daten daraus zu verknüpfen, um ein hübsches Recordset zu erhalten. Holen Sie sich den Originaldatensatz und dann den Datensatzsatz für die Entität. Wenn Sie versucht sind, sich den Tabellen anzuschließen, haben Sie wahrscheinlich den zweiten Fehler gemacht, wie oben erwähnt.

Spencer Ruport
quelle
Ich verwende dieses Modell in einem aktuellen Projekt von mir und es war wirklich vorteilhaft. Sie generieren auch de-normalisierte Ansichten der Daten, wenn dies für eine einfache Abfrage / Datenbindung erforderlich ist.
Dillie-O
Ja, wenn sie sorgfältig angewendet werden, sind sie ziemlich mächtig.
Spencer Ruport
1
Beachten Sie die Warnungen im Wikipedia-Artikel. Schlecht gemacht, können Ganzheit / Wert-Paare ein System ermorden, wie viele im Internet verstreute Katastrophengeschichten belegen. Wenn sich benutzerdefinierte Daten auf mehr Elemente als nur auf das "direkte übergeordnete Element" beziehen müssen, ist es wahrscheinlich viel besser, diese Daten als neue Zeilen oder Tabellen zum Modell hinzuzufügen. Mein Hauptratschlag wäre, sich sehr zu bemühen, ein solches System nicht zu überbeanspruchen.
Philip Kelley
Interessante Lektüre auf Wikipedia. Das EAV-Modell ist sicherlich ein zweischneidiges Schwert. Meine +1 gibt diesem Ansatz einen Namen und die Tatsache, dass der Wikipedia-Artikel ziemlich gut ist. Persönlich kann ich viele Probleme sehen, wenn ich diesem Ansatz folge, aber ich kann sehen, wo es nützlich wäre. Ich denke, eine XML-Spalte ist eine ernsthafte Alternative, trotz der Aussagen im Wikipedia-Artikel.
RichardOD
Ich denke, jeder hat gute Punkte zu diesem Thema gemacht. Für mich: XML ist nur eine weitere Darstellung von Zeilen in einer Datenbank. Das Hinzufügen einer XML-Spalte entspricht also einer winzigen Datenbank in einer Zeile. :(
Diederik
5

Wenn Sie mit einer objektorientierten Sprache entwickeln, sprechen wir hier von adaptiven Objektmodellen . Es gibt einige Artikel darüber, wie Sie sie in oo-Sprachen implementieren können, aber nicht so viele Informationen darüber, wie die Datenspeicherseite gestaltet werden soll.

In der Firma, in der ich arbeite, haben wir das Problem gelöst, indem wir eine relationale Datenbank zum Speichern von AOM-Daten verwendet haben. Wir haben eine zentrale Entitätstabelle zur Darstellung aller verschiedenen "Entitäten" in der Domäne, wie Personen, Netzwerkgeräte, Unternehmen usw. Wir speichern die tatsächlichen "Formularfelder" in typisierten Datentabellen, sodass wir eine Tabelle für haben Zeichenfolgen, eine für Daten und so weiter. Alle Datentabellen haben einen Fremdschlüssel, der auf die Entitätstabelle verweist. Wir benötigen auch Tabellen, um die Typenseite darzustellen, dh welche Art von Attributen (Formularfeldern) bestimmte Entitäten haben können, und diese Informationen werden verwendet, um die Daten in Datentabellen zu interpretieren.

Die Vorteile unserer Lösung sind, dass alles ohne Codeänderungen modelliert werden kann, einschließlich Referenzen zwischen Entitäten, Mehrfachwerten usw. Es ist auch möglich, Geschäftsregeln und Validierungen zu Feldern hinzuzufügen, und sie können in jeder Form wiederverwendet werden. Nachteile sind, dass das Programmiermodell nicht sehr einfach zu verstehen ist und die Abfrageleistung schlechter ist als bei einem typischeren DB-Design. Eine andere Lösung als die relationale Datenbank hätte für AOM besser und einfacher sein können.

Das Erstellen eines guten AOM mit einem funktionierenden Datenspeicher ist eine Menge Arbeit, und ich würde es nicht empfehlen, wenn Sie keine hochqualifizierten Entwickler haben. Vielleicht wird es eines Tages eine Betriebssystemlösung für diese Art von Anforderungen geben.

Benutzerdefinierte Felder wurden bereits in SO erläutert:

Kaitsu
quelle
3

So etwas wie Option 3 ist der richtige Weg und ich habe diese Methode bereits verwendet. Erstellen Sie eine einzelne Tabelle, um zusätzliche Eigenschaften und ihre entsprechenden Werte zu definieren. Dies wäre eine 1-N-Beziehung zwischen Ihrer Customer- und der CustomerCustomField-Tabelle (jeweils). Ihre zweite Frage zum Definieren von Beziehungen zu benutzerdefinierten Eigenschaften sollte Sie sich überlegen. Das erste, was Ihnen in den Sinn kommt, ist das Hinzufügen eines DataSource-Felds, das die Tabelle enthält, an die der Eigenschaftswert gebunden ist. Im Wesentlichen würde Ihr CustomerCustomField also so aussehen:

  1. Kundennummer
  2. Eigentum
  3. Wert
  4. ValueDataSource (nullbar)

Auf diese Weise können Sie entweder an eine bestimmte Datenstruktur binden oder einfach ungebundene Werte angeben. Sie können dieses Modell weiter normalisieren, aber so etwas könnte funktionieren und sollte einfach genug sein, um im Code verarbeitet zu werden.


quelle
3

Option 4 oder 5 wäre meine Wahl. Wenn Ihre Daten wichtig sind, würde ich Ihre Typinformationen mit Option 3 nicht wegwerfen. (Sie könnten versuchen, die vollständige Typprüfung selbst zu implementieren, aber es ist eine ziemlich große Aufgabe, und das Datenbankmodul erledigt dies bereits für Sie.)

Einige Gedanken:

  • Stellen Sie sicher, dass Sie CustomFieldseine DataTypeSpalte haben.
    • Verwenden Sie eine UDF-basierte Prüfbedingung für, CustomFieldValuesum sicherzustellen, dass die durch angegebene Spalte CustomFields.DataTypenicht null ist.
    • Sie möchten auch eine Standardprüfungsbeschränkung, um sicherzustellen, dass Sie genau einen Wert ungleich Null haben.
  • In Bezug auf Fremdschlüssel würde ich diese als separate modellieren DataType.
    • Jede mögliche tabellenübergreifende Referenz würde eine eigene Spalte erfordern. Dies ist gut, da die referenzielle Integrität erhalten bleibt.
    • Sie müssten diese Beziehungen ohnehin im Anwendungscode unterstützen, sodass die Tatsache, dass sie in der Datenbank fest codiert sind, die Funktionalität nicht einschränkt.
    • Dies passt auch gut zu Ihrem ORM, wenn Sie eines verwenden.
  • Verwenden Sie für Option 5 Zwischentabellen, um die Beziehungen zu modellieren.
    • Sie hätten immer noch eine CustomerCustomFieldValue, aber stattdessen nur CustomerIDund CustomFieldValueIDSpalten.
  • Denken Sie bei jedem Schritt lange und gründlich über Ihre Einschränkungen nach. Dies ist eine knifflige Angelegenheit, und ein Fehltritt kann auf der ganzen Linie zu Chaos führen.

Ich verwende dies in einer Anwendung, die sich derzeit in der Entwicklung befindet. Es gab noch keine Probleme, aber EAV-Designs erschrecken mich immer noch vor Tageslicht. Sei bloß vorsichtig.

Abgesehen davon kann XML auch eine gute Wahl sein. Ich weiß aus direkter Erfahrung nicht so viel darüber, aber es war eine der Optionen, die ich beim Starten des Datenentwurfs in Betracht gezogen habe, und es sah ziemlich vielversprechend aus.

WCWedin
quelle
0

Wenn diese 'zusätzlichen' Felder zufällig sind und keine Suche nach ihnen durchführen möchten, entscheide ich mich normalerweise für Option 2 (aber JSON ist besser als XML). Wenn benutzerdefinierte Felder durchsucht werden sollen, ist Option 3 nicht schwer durchzuführen, und normalerweise kann das SQL-Optimierungsprogramm eine angemessene Leistung erzielen.

Javier
quelle
0

Ich arbeite derzeit an einem Projekt mit demselben Problem und habe mich für Option 3 entschieden. Ich habe jedoch ein FieldType-Feld und ein ListSource-Feld hinzugefügt, falls FieldType = "list". Das ListSource-Feld kann eine Abfrage, eine SQL-Ansicht, ein Funktionsname oder etwas sein, das zu einer Liste von Optionen für die Liste führt. Das größte Problem beim Versuch, Felder wie dieses in meiner Situation zu speichern, besteht darin, dass sich diese Feldliste ändern kann und die Benutzer die Daten später bearbeiten können. Was tun, wenn sich die Feldliste geändert hat und sie bearbeitet werden? Meine Lösung für dieses Szenario bestand darin, die Bearbeitung nur zuzulassen, wenn sich die Liste nicht geändert hat, und schreibgeschützte Daten anzuzeigen, wenn dies der Fall ist.

jbair
quelle