Konfigurationsdaten: Einzeilige Tabelle vs. Name-Wert-Paar-Tabelle

64

Angenommen, Sie schreiben eine Anwendung, die vom Benutzer konfiguriert werden kann. Zum Speichern dieser "Konfigurationsdaten" in einer Datenbank werden üblicherweise zwei Muster verwendet.

  1. Die einzeilige Tabelle

      CompanyName  |  StartFullScreen  |  RefreshSeconds  |  ...
    ---------------+-------------------+------------------+--------
      ACME Inc.    |        true       |       20         |  ...
    
  2. Die Name-Wert-Paar- Tabelle

      ConfigOption   |   Value
    -----------------+-------------
     CompanyName     | ACME Inc.
     StartFullScreen | true (or 1, or Y, ...)
     RefreshSeconds  | 20
     ...             | ...
    

Ich habe beide Optionen in der freien Natur gesehen und beide haben offensichtliche Vor- und Nachteile, zum Beispiel:

  • Die einzeiligen Tabellen begrenzen die Anzahl der Konfigurationsoptionen, die Sie haben können (da die Anzahl der Spalten in einer Zeile normalerweise begrenzt ist). Jede zusätzliche Konfigurationsoption erfordert eine Änderung des DB-Schemas.
  • In einer Name-Wert-Paar-Tabelle ist alles "string-typisiert" (Sie müssen Ihre Booleschen / Datums- / usw. Parameter codieren / decodieren).
  • (viel mehr)

Gibt es innerhalb der Entwicklungsgemeinschaft einen Konsens darüber, welche Option vorzuziehen ist?

Heinzi
quelle
2
Es gibt keinen Grund, warum der vertikale Ansatz nicht unterschiedliche Datentypen haben kann. Fügen Sie pro Zeile eine int-, float- und text-Spalte hinzu. Speichern / Laden von Werten mithilfe typspezifischer Funktionen, z. B. 'SaveConfigInt (' field ', n)'
GrandmasterB
4
Hierfür gibt es eine hervorragende StackOverflow-Frage, und die beste Antwort gibt Vor- und Nachteile für beide Ansätze. stackoverflow.com/questions/2300356/…
Kevin
1
Ansatz 3: Einzelne Spalte / Einzelne Zeile mit einem einfachen Datenaustauschformat wie JSON oder YAML. Kombiniert die Vorteile beider Ansätze.
Schlamar
Was ist mit einzeiligen Tabellen mit komplexen Daten, die xml / json enthalten, wie <config> <CompanyName> ACME Inc. </ CompanyName> <StartFullScreen> true </ StartFullScreen> 20 <RefreshSeconds> </ RefreshSeconds> </ config> und Objekt in Business-Schicht validieren?
John
1
@ John: Gute Idee, wenn hierarchische Strukturen benötigt werden. Wenn nicht, ist es nur Option 2 mit zusätzlicher Komplexität.
Heinzi

Antworten:

15

Ich persönlich bevorzuge die einzeiligen Tabellen für die meisten Dinge. Es ist zwar wahr, dass es weniger flexibel ist, es sei denn, Sie erwarten dynamisches Verhalten, aber es ist durchaus akzeptabel, später zusätzliche Spalten hinzuzufügen, wenn dies erforderlich ist. In gewisser Weise entspricht dies der Verwendung eines Wörterbuchs / einer Karte, um Name-Wert-Paare zu halten, im Gegensatz zu Klassenmitgliedern beim Programmieren. Zugegeben, es ist keine perfekte Metapher, aber wenn man darüber nachdenkt, sind viele Vor- und Nachteile gleich.

Würden Sie also ein Wörterbuch / eine Karte über Klassenmitgliedern verwenden? Wahrscheinlich nur, wenn Sie Grund zu der Annahme hatten, dass die Menge der darzustellenden Daten vollständig anpassbar ist, ähnlich wie bei einer Name-Wert-Paartabelle.

Neil
quelle
Was ist, wenn die zu speichernden Daten benutzerdefiniert sind? Stellen Sie sich eine Benutzeroberfläche vor, in der der Benutzer ein "Feld" erstellen kann, indem er die Feldbezeichnung, den Datentyp usw. angibt. Dies würde bedeuten, dass DDL-Anweisungen aus Code ausgeführt werden. Würden Sie noch mit Option 1 gehen?
Devanalyst
1
@devanalyst Nein, wenn sich die Daten von Komponente zu Komponente ändern könnten, wäre es nicht sinnvoll, eine statische Tabelle zu erstellen, um sie darzustellen. In diesem Fall ist es besser, die zweite Option zu verwenden.
Neil,
12

Im Allgemeinen würde ich Option 2 wählen, ABER ich hätte mehrere Spalten, um den Datentyp zu erzwingen

ConfigOption   |   textValue    |   DateValue   |   NumericValue

Option 1 hat den zusätzlichen Vorteil, dass Sie sehr einfach ganze Konfigurationen "tauschen" können, indem Sie eine ActiveSpalte hinzufügen .

Idioten
quelle
Wenn Sie zulassen möchten, dass Konfigurationen deaktiviert werden (für Option 1), geben Sie mindestens einen activatedOnZeitstempel an, damit Sie erkennen können, wann der Zeitstempel aktiviert wurde. Und wenn Sie sich für Option 2 entscheiden ... was passiert, wenn am Ende Werte in mehreren Spalten gespeichert werden (oder es ist Oracle, wo (anscheinend) Null und eine leere Zeichenfolge äquivalent sind)?
Clockwork-Muse
1
@ X-Zero: Das Speichern mehrerer Konfigurationen wird normalerweise zu Testzwecken durchgeführt, aber ein Zeitstempel kann nicht schaden. Der Aufruf Config Maintenance, um den Wert zu erhalten, würde wissen, welche Spalte zu überprüfen ist. Wenn Sie wirklich wollten, könnten Sie eine Spalte für den Datentyp hinzufügen. Aber ich denke, das ist vorbei ...
Morons
5
Ein EATV-Schema (Entity-Attribute-Type-Value) unterbricht die dritte Normalform. Die Spalte Typ ist nur indirekt mit dem Primärschlüssel der Tabelle über die Spalte Wert verbunden, die in der Spalte Typ beschrieben wird. Darüber hinaus lassen sich durch Speichern und Instanziieren dynamischer Typen nicht viele Probleme lösen. Wenn eine GetConfigValue () -Methode einen beliebigen Typ zurückgeben kann, muss sie Object zurückgeben (oder irgendwie den erwarteten Typ erhalten), der zur Laufzeit noch ausgewertet werden muss.
KeithS
5
Jedes Mal, wenn Option 1 in Software implementiert wurde, die ich gesehen habe, musste sie in Option 2 konvertiert werden. Option 2 ist im Laufe der Zeit einfacher zu warten, erfordert nur einen Fingertipp mehr Zeit, um sie beim ersten Mal korrekt zu implementieren. Option 1 ist schnell und einfach zu implementieren, aber die Wartung im Laufe der Zeit ist schrecklich, es sei denn, Ihre Software ist winzig und kann nicht wachsen.
Jimmy Hoffa
8

Für mich hängt es davon ab, wie Sie sie konsumieren möchten, ob Sie einreihig oder mit EAV arbeiten.

Die Stärke von EAV besteht darin, dass neue Daten hinzugefügt werden können, ohne die Struktur zu ändern. Dies bedeutet, dass Sie, wenn Sie einen neuen Konfigurationswert möchten, diesen einfach zur Tabelle hinzufügen und an der gewünschten Stelle im Code abrufen und der Domäne, dem Schema, der Zuordnung und den DAL-Abfragen kein neues Feld hinzufügen müssen , usw.

Der Nachteil ist, dass es nur die geringste Struktur aufweist, sodass Sie pessimistisch mit den Daten umgehen müssen. Bei jeder Verwendung eines Konfigurationswerts muss davon ausgegangen werden, dass der Wert nicht vorhanden ist oder nicht das richtige Format aufweist, und muss sich entsprechend verhalten, wenn dies nicht der Fall ist. Ein Konfigurationswert kann möglicherweise nicht in ein double, ein int oder ein char umgewandelt werden. Es kann null sein. Möglicherweise gibt es überhaupt keine Zeile für den Wert. Um dies zu umgehen, muss normalerweise ein einziger gültiger "Standard" -Wert für alle Konfigurationswerte eines bestimmten In-Code-Typs vorhanden sein ( äußerst selten; häufig ist der Standardwert für die Verwendung von Code genauso problematisch wie überhaupt keiner) oder Behalten Sie ein fest codiertes Wörterbuch mit Standardwerten bei (das sich jedes Mal ändern muss, wenn eine neue Spalte hinzugefügt wird, wodurch der Hauptvorteil des EAV-Speichers überflüssig wird).

Eine einzelne breite Reihe ist so ziemlich das Gegenteil. Sie ordnen es einer einzelnen Instanz eines Konfigurationsobjekts mit einem Feld / einer Eigenschaft für jeden vorhandenen Konfigurationswert zu. Sie wissen genau, welchen Typ diese Werte zur Kompilierungszeit haben sollten, und Sie "fallen schnell" in der DAL aus, wenn eine Konfigurationsspalte nicht vorhanden ist oder keinen Wert des richtigen Typs hat, wodurch Sie einen Ort zum Abfangen von Ausnahmen erhalten bei Problemen mit dem Abrufen der Konfiguration / der Flüssigkeitszufuhr.

Der Hauptnachteil besteht darin, dass für jeden neuen Wert eine strukturelle Änderung erforderlich ist. Neue DB-Spalte, neue Spalte in der DAL (entweder das Mapping oder die SQL-Abfragen / SPs), neue Domänenspalte, alles erforderlich, um die Verwendung ordnungsgemäß zu testen.

Die richtige Situation, in der eine von diesen verwendet wird, ist die Situation, in der die Nachteile gemindert werden. In den meisten Situationen für die Konfigurationscodierung war für mich eine einzeilige Implementierung erforderlich. Dies liegt hauptsächlich daran, dass Sie, wenn Sie einen völlig neuen Konfigurationswert einführen, der das Verhalten eines Teils Ihres Programms steuert, den Code bereits ändern müssen, um den neuen Konfigurationswert zu verwenden. Warum nicht zum Konfigurationsobjekt wechseln und den zu verwendenden Wert hinzufügen ?

Kurz gesagt, ein EAV-Schema zum Speichern der Konfiguration löst das Problem, das es zu lösen vorgibt, nicht wirklich, und die meisten Problemumgehungen für die darin enthaltenen Probleme betreffen DRY.

KeithS
quelle
3

Speziell für Konfigurationswerte würde ich sagen - gehen Sie mit der einzelnen Zeile. Wie oft werden sich diese Spalten ändern, es sei denn, Sie befinden sich gerade in der Entwicklung?

Es ist wahrscheinlich am besten, den Datentyp der Werte und nicht den Code für die Erweiterbarkeit zu sichern, die Sie in den Ausfallzeiten zwischen großen (r) Releases wahrscheinlich nicht haben. Außerdem ist das Hinzufügen oder Entfernen einer einzelnen Spalte die einfachste Migration, die es gibt. Beim Erstellen einer neuen Konfigurationsoption sind keine Kopfschmerzen zu erwarten.

Außerdem haben Sie gesagt, "Benutzer" können diese Optionen konfigurieren, ohne eine Obergrenze zu setzen. Handelt es sich um Benutzerkonfigurationen? In diesem Fall werde ich noch stärker argumentieren, dass sich die Konfigurationsoptionen in den Spalten befinden sollten - eine einzelne Zeile pro Benutzer. Das erspart später viele Wartungsprobleme.

Izkata
quelle
2

Wenn Ihre Clients JSON-Fragmente verarbeiten können (das sind nicht nur Arrays und Wörterbücher, sondern auch einfache Zeichenfolgen, Zahlen, Boolesche Werte und Nullwerte), können Sie eine mehrzeilige Tabelle mit dem Optionsnamen und einem Zeichenfolgenwert haben, der JSON enthält. Dadurch können Sie auch strukturierte Werte speichern, und der Code für die Verarbeitung dieser sollte bereits vorhanden sein.

Wenn Ihre Clients keine JSON-Fragmente verarbeiten können, holen Sie sich neue Clients.

gnasher729
quelle
1

Einreihige Vorteile: Gut definiert. Nachteile: Das Ändern der Konfiguration kann schmerzhaft sein. DB-Migrationen etc ..

Entity-Value-Vorteile: Superflexibel, unterstützt die Weiterentwicklung Ihrer Konfiguration. Nachteile: Referentielle Integrität? Weitere Überprüfungen in Ihrem Code, um festzustellen, ob die Eigenschaft vorhanden ist, bevor Sie etwas daran ändern können.

Ich würde Ansatz 2 wählen, der von einer nicht relationalen Datenbank wie Mongo unterstützt wird. Wenn es etwas gibt, dessen Sie sich sicher sein können, ändert es sich.

JVXR
quelle
1

Verwende beide!

Sortieren Sie, welche Optionen mehrere Instanzen haben können und welche Optionen generisch sind.

Die einzeilige Tabelle (Konfigurationen)

  id  |  company_name  |  start_fullscreen  |  refresh_seconds  |  ...
------+----------------+--------------------+-------------------+-------
  4   |  ACME Inc.     |  true              |  20               |  ...

Die Name-Wert-Paar-Tabelle (Optionen)

  name             |  value          | update_time  
-------------------+-----------------+--------------
  generic_option_1 |  Option 1 Value | timestamp    
  generic_option_2 |  Option 2 Value | timestamp    
  generic_option_3 |  Option 3 Value | timestamp    
  configuration    |  4              | timestamp    
  ...              |  ...            | ...          

Ich denke das ist flexibler.

Andrew Luca
quelle