Ist es eine schlechte Praxis, benutzerdefinierte Felder zuzulassen?

17

Wird es allgemein als schlechte Praxis angesehen, vom Benutzer erstellte Felder in einer Datenbank für eine Webanwendung zuzulassen?

Zum Beispiel erstelle ich eine Web-App für meine Frau, in der sie ihre eigenen Felder für verschiedene Artikel definieren möchte. Ich wollte ihr erlauben, Artikelkategorien zu erstellen und "Features" zu diesen Kategorien hinzuzufügen. Features sind nur Schlüssel / Werte, die als Zeichenfolgen gespeichert werden. Wenn sie beispielsweise eine Kategorie mit dem Namen "Audio-CDs" hätte, könnte sie Funktionen für Dinge wie "Künstler", "Titel" usw. hinzufügen. In einer anderen Kategorie wie "Möbel" könnte sie Funktionen für Dinge wie "Material" hinzufügen "(Holz, Kunststoff usw.) Dann kann jeder Artikel zu einer (oder mehreren) Kategorien gehören und diese Funktionen zum Artikel hinzufügen.

Ich kann Probleme feststellen, bei denen die Suche nach diesen Features Zeichenfolgenvergleiche erfordert, keine Validierung von Daten usw. Nach einer agilen Methodik ist es möglicherweise besser, sie nur mit neuen Kategorien und Attributen auszustatten und neue Tabellen zu erstellen während wir gehen. In meinem Beispiel ist es eine kleine Nutzerbasis (2 von uns) und die Menge der erstellten Datensätze wäre klein, also nicht schlecht.

Aber wie gehen die Leute im "echten Leben" generell damit um?

zako42
quelle
4
Haben Sie darüber nachgedacht, eine dokumentenorientierte Datenbank wie MongoDB zu verwenden? Sie können ein Dokument pro Typ speichern, das als Schema fungiert und auch bearbeitet werden kann (wahrscheinlich manuell, angesichts des geringen Umfangs des Projekts).
Andy Hunt
@AndyBursh eines der "lustigen" Bits mit aktuellen Postgres ist der "json" -Datentyp ( Link ). Ein solcher Ansatz würde es einem ermöglichen, benutzerspezifische Felder in diesen Daten, ähm, dokumentieren, ähm, was auch immer, zu speichern und dann den Rest der Felder für Dinge zu verwenden, für die Sie die richtigen Indizes haben und dergleichen. Dies hängt jedoch von der Verwendung ab und es ist schwer zu sagen, ob dies für eine bestimmte Anwendung gut funktionieren würde oder nicht. Aber es ist etwas zu beachten.
all: tolle diskussion, danke für den ganzen einblick! @AndyBursh Ich habe von MongoDB gehört, aber nie richtig darüber gelesen. Klingt nach einem weiteren
Heimprojekt,

Antworten:

19

Wenn Sie zu "benutzerdefinierten Feldern" gelangen, wie sie häufig in Bug - Trackern, Kundenressourcenverwaltung und ähnlichen Geschäftstools zu finden sind, werden sie nicht mit einer Tabelle mit Bajillion - Feldern unterstützt (wenn dies der Fall ist, ist dies wahrscheinlich ein Problem seine eigene).

Stattdessen finden Sie Tabellenentwürfe für Entitätsattributwerte und das zugehörige Verwaltungstool zum Verwalten der gültigen Attribute.

Betrachten Sie die folgende Tabelle:

  + -------------- +
  | sache |
  | -------------- |
  | id |
  | Typ |
  | desc |
  | attr1 |
  | attr2 |
  | attr3 |
  | attr4 |
  | attr5 |
  + -------------- +

Dies ist, nachdem Sie einige Attribute hinzugefügt haben. Anstatt so zu attr1tun, als würde es lesen artistoder tracksoder genreoder welche Attribute das Ding hat. Und statt 5, was wäre, wenn es 50 wäre? Das ist natürlich nicht zu handhaben. Außerdem ist eine Aktualisierung des Modells und eine erneute Bereitstellung der Anwendung erforderlich, um ein neues Feld bearbeiten zu können. Nicht ideal.

Betrachten Sie nun die folgende Tabellenstruktur:

  + -------------- + + --------------- + + ------------- +
  | sache | | thing_attr | | attr |
  | -------------- | | --------------- | | ------------- |
  | id | <--- + | thing_id (fk) | +> | id |
  | Typ | | attr_id (fk) | + - + | name |
  | desc | | Wert | | |
  + -------------- + + --------------- + + ------------- +

Sie haben Ihr Ding mit seinen Grundgebieten. Sie haben zwei weitere Tische. Eins mit den Attributen. Jedes Feld ist eine Zeile in der attrTabelle. Und dann gibt es das thing_attrmit einem Paar Fremdschlüsseln, die sich auf den thingTisch und den attrTisch beziehen . Und dies hat dann ein Wertefeld, in dem Sie den Wert des Feldes für diese Entität speichern.

Und jetzt haben Sie eine Struktur, in der die ATTR-Tabelle zur Laufzeit aktualisiert und neue Felder hinzugefügt (oder entfernt) werden können, ohne dass dies erhebliche Auswirkungen auf die Gesamtanwendung hat.

Die Abfragen sind etwas komplexer und die Validierung wird auch komplexer (entweder flippige gespeicherte Prozeduren oder alle Clients). Es ist ein Kompromiss im Design.

Berücksichtigen Sie auch die Situation, in der Sie eines Tages eine Migration durchführen müssen und Sie zur Anwendung zurückkehren und feststellen, dass es jetzt ein halbes Dutzend oder mehr Attribute als das ursprünglich verteilte Schema gibt. Dies führt zu hässlichen Migrationen und Upgrades, bei denen die Entity Attribute Value-Tabelle bei korrekter Verwendung sauberer sein kann. (Nicht immer, kann aber sein.)


Gibt es Nachteile, nur das Schema zur Laufzeit zu ändern? Wenn der Benutzer glaubt, dass eine Sache ein neues Attribut benötigt, fügen Sie der Tabelle einfach dynamisch eine Spalte hinzu?

Wenn Sie mit der entsprechenden Version der nosql-Datenbank arbeiten, können Sie dies wahrscheinlich tun (beachten Sie, dass die entsprechende Version der nosql wahrscheinlich ein Schlüsselwertspeicher ist, also die EAV-Tabelle für die oben beschriebenen relationalen). ohne zu viel Mühe. Es enthält jedoch alle Kompromisse für nosql, die an anderer Stelle ausführlich beschrieben werden.

Wenn Sie stattdessen an einer relationalen Datenbank arbeiten, benötigen Sie das Schema. Das dynamische Hinzufügen der Spalte bedeutet, dass eine Teilmenge der folgenden Dinge zutrifft:

  • Sie programmieren Metadatenbanken. Anstatt in der Lage zu sein, diese Spalte mit einem netten ORM sauber diesem Feld zuzuordnen, tun Sie wahrscheinlich Dinge wie select *und führen dann einen komplexen Code aus, um herauszufinden, was die Daten tatsächlich sind (siehe Javas ResultSetMetaData ) und diesen dann in einer Karte zu speichern ( oder ein anderer Datentyp - aber keine netten Felder im Code). Dies wirft dann einiges an Typ- und Typosicherheit weg, die Sie mit dem traditionellen Ansatz haben.
  • Sie haben den ORM wahrscheinlich aufgegeben. Dies bedeutet, dass Sie Raw-SQL für den gesamten Code schreiben, anstatt das System die Arbeit für Sie erledigen zu lassen.
  • Sie haben es aufgegeben, saubere Upgrades durchzuführen. Was passiert, wenn der Kunde ein Feld mit einem Namen hinzufügt, den auch Ihre nächste Version verwendet? Auf der Matchmaking-Site wurde das Upgrade, das ein hasdateFeld zum Speichern eines Zeitstempels hinzufügen möchte, bereits wie hasdateein Boolescher Wert für ein erfolgreiches Match definiert ... und das Upgrade wird abgebrochen.
  • Sie vertrauen darauf, dass der Kunde das System nicht durch ein reserviertes Wort zerstört, das auch Ihre Abfragen zerstört ... irgendwo.
  • Sie haben sich an eine Datenbankmarke gebunden. Die DDL verschiedener Datenbanken ist unterschiedlich. Datenbanktypen sind das einfachste Beispiel dafür. varchar2vs textund dergleichen. Ihr Code zum Hinzufügen der Spalte funktioniert unter MySQL, jedoch nicht unter Postgres, Oracle oder SQL Server.
  • Haben Sie den Kunden vertrauen auf die Daten tatsächlich fügen gut ? Sicher, der EAV ist alles andere als ideal, aber jetzt haben Sie einige schreckliche undurchsichtige Tabellennamen, die Sie vom Entwickler nicht hinzugefügt haben, mit dem falschen Indextyp (falls vorhanden), ohne dass im Code Einschränkungen hinzugefügt wurden, wo dies erforderlich ist sein und so weiter.
  • Sie haben dem Benutzer, der die Anwendung ausführt, Berechtigungen zum Ändern des Schemas erteilt. Little Bobby Drop Tables ist nicht möglich, wenn Sie sich auf SQL und nicht auf DDL beschränken (Sie können dies natürlich auch tun delete * from students, aber Sie können die Datenbank nicht auf schlimme Weise durcheinander bringen). Die Anzahl der Probleme, die beim Schemazugriff aufgrund eines Unfalls oder böswilliger Aktivitäten auftreten können, steigt in die Höhe.

Das läuft wirklich darauf hinaus, "es nicht zu tun". Wenn Sie dies wirklich möchten, verwenden Sie ein bekanntes Muster der EAV-Tabellenstruktur oder eine Datenbank, die dieser Struktur vollständig zugeordnet ist. Lassen Sie nicht zu, dass Personen beliebige Felder in einer Tabelle erstellen. Die Kopfschmerzen sind es einfach nicht wert.

DougM
quelle
4
Sie haben auch die Datenbank neu erfunden.
user253751
1
@immibis es wurde eine Ebene hinzugefügt, in der der Benutzer verwalten kann, ohne den Rest der Datenbank zu entstellen oder eine erneute Bereitstellung zum Aktualisieren des Modells zu erfordern.
1
@immibis EAV wird seit Jahren in relationalen Datenbankkreisen heiß diskutiert. Theoretisch ist das nicht notwendig, aber in der Praxis kann man bestimmte Dinge nicht ohne es tun.
Ross Patterson
1
@ ShivanDragon, das zum NoSQL-Ansatz übergeht. Der Dokumentenspeicher speichert nur Dokumente und legt kein Schema fest. Aus diesem Grund liegt das Hinzufügen und Entfernen von Feldern und das Parsen der Dokumente völlig außerhalb des Bereichs der Datenbank selbst (und Sie haben Ihr Modell geschrieben, um dies zu berücksichtigen). Es handelt sich um eine völlig andere Reihe von Kompromissen als die relationalen Datenbankkompromisse für eine EAV-Struktur.
1
Weiterführende Chatdiskussion
5

Das gut zu machen ist schwer.

Für eine einmalige Anwendung wie die, die Sie planen, können Sie natürlich einfach eine Spalte für jedes Feld hinzufügen und eine Benutzeroberfläche bereitstellen, die die Felddefinition für ungeübte Benutzer sicherer macht, als ihnen eine SQL-Befehlszeile zu geben. Sie können auch dem gefürchteten Muster " Entity-Attribute-Value" folgen , das eine klassische, wenn auch etwas beängstigende Antwort auf diese Art von Problem darstellt. Das Erstellen der Benutzeroberfläche zum Definieren von EAV-Feldern ist in der Regel sehr viel komplexer als für Datenbankspalten, und die Abfragen können ziemlich unübersichtlich werden. Bei einer großen Anzahl von Feldern ( dh Schemata mit sehr geringer Matrixdichte) ist dies möglicherweise die einzige Möglichkeit die Arbeit erledigt.

Ross Patterson
quelle
Zusammenfassend: kleines Projekt == KISS. Beweglich bis zum Boden.
Encaitar
Das Problem bei der Aktualisierung von Datenbanktabellen besteht darin, dass die Abfrage zum Ändern der Tabelle in Abhängigkeit von der Datenmenge und den erforderlichen Indizes (benutzerdefinierte Felder erfordern häufig Suchfunktionen) sehr viel Zeit in Anspruch nehmen kann. Kurz gesagt, MySQL und andere relationale Datenbanken sind einfach kein gutes Medium für diese Art von Anforderung.
Oddman
0

Ich bin in letzter Zeit auf ein ähnliches Kreuz gestoßen.

Ich habe 2 Tische gemacht.

1: table Objects 
    Id , name, type

Er ist alle deine Objekte. Sie setzen den Namen davon.

Und ein Typ dieses Objekts: - Für mich waren die verfügbaren Typen Inventar, Inventargegenstand, Büro.

Und das übliche Setup war n Elemente sind untergeordnet oder Inventar, das auch untergeordnet zu Office ist, und ich habe eine Verknüpfungstabelle verwendet, um Objekte miteinander zu verknüpfen

2 table settings 
     organization_Id , title, value , type

Die Einstellungstabelle enthält alle Feldnamen für diesen bestimmten Objekttyp und den Wert im Wert.

Beispielimmobilien von Büro

Ort, Telefon, Arbeitszeit

Und für Gegenstände

  • Menge
  • Preis
  • Barcode

Alle diese Eigenschaften werden von Ihrem Modell erzwungen und in der Einstellungstabelle als separate Zeilen gespeichert. Verwenden Sie jedoch die Option "Ersetzen, nicht einfügen", um mehrere Zeilen für dasselbe Feld zu vermeiden.

Wann immer ich ein Büro haben möchte, lade ich es einfach mit all seinen Beziehungen und Einstellungen, in denen die Einstellungen object_I'd sind (angeforderte Objekte).

Danach schwenke ich alle Zeilen aus den Einstellungen und das wars.

Und falls ich wollte, dass eine Einstellung spezifisch für einen Artikel in einem Inventar ist (nicht global), setze ich object_I'd = Ich würde aus der object_objects-Beziehungstabelle und setze settings.type = relation_setting

Ich hoffe, Sie verstehen, was ich meine. Ich werde versuchen, die Antwort neu zu formatieren, wenn ich an einen Laptop komme

Zalaboza
quelle
2
Profi-Tipp - Posten Sie in diesem Forum nicht von Ihrem Handy aus. Die automatische Korrektur macht Teile Ihres Beitrags unlesbar.
BobDalgleish
Haha schöne Beobachtung :)
Zalaboza
0

Ist es eine schlechte Praxis, benutzerdefinierte Felder zuzulassen?

Nein, es ist keine schlechte Praxis. Es ist ziemlich verbreitet. In OO-Begriffen wird dies Vererbung genannt. Sie haben eine Basisklasse inventoryItem und zwei geerbte Klassen AudioCD und furniture.

Aber wie gehen die Leute im "echten Leben" generell damit um?

Sie müssen entscheiden, wie Inventarartikel, AudioCD und Möbel in der Datenbank gespeichert werden.

Wenn eine einfache Abfrage für Sie am wichtigsten ist und DB-Space / Normalisierung keine Rolle spielt, würden Sie das Schema "Tabelle pro Hierarchie" implementieren.

Wenn Platz / Normalisierung für Sie am wichtigsten ist und kompliziertere Abfragen für Sie kein Problem darstellen, würden Sie das Schema "Tabelle pro Typ" implementieren.

Weitere Informationen finden Sie unter Vererbung von Dotnet-Tabellen pro Typ im Vergleich zu Tabellen pro Hierarchie oder Java-Hibernate .

k3b
quelle
Ich weiß nicht, ob dies die Frage anspricht. Der Benutzer ändert den Code nicht, um neue Klassen zu erstellen
Colin D