Verwenden einer einzeiligen Konfigurationstabelle in der SQL Server-Datenbank. Schlechte Idee?

144

Bei der Entwicklung einer Warenkorbanwendung musste ich Einstellungen und Konfigurationen basierend auf den Einstellungen und Anforderungen des Administrators speichern. Diese Informationen können Unternehmensinformationen, Versandkonto-IDs, PayPal-API-Schlüssel, Benachrichtigungseinstellungen usw. sein.

Es erscheint höchst unangemessen, eine Tabelle zum Speichern einer einzelnen Zeile in einem relationalen Datenbanksystem zu erstellen.

Was ist der geeignete Weg, um diese Informationen zu speichern?

Hinweis: Mein DBMS ist SQL Server 2008 und die Programmierschicht ist mit ASP.NET (in C #) implementiert.

David Murdoch
quelle

Antworten:

188

Ich habe dies in der Vergangenheit auf zwei Arten getan - eine einzelne Zeilentabelle und eine Schlüssel / Wert-Paartabelle - und jeder Ansatz hat positive und negative Aspekte.

Einzelne Reihe

  • positiv: Die Werte werden im richtigen Typ gespeichert
  • positiv: es ist einfacher, mit Code umzugehen (aus den oben genannten Gründen)
  • positiv: Standardwerte können für jede Einstellung einzeln angegeben werden
  • negativ: Zum Hinzufügen einer neuen Einstellung ist eine Schemaänderung erforderlich
  • negativ: Die Tabelle kann sehr breit werden, wenn viele Einstellungen vorhanden sind

Schlüssel / Wert-Paar

  • positiv: Für das Hinzufügen neuer Einstellungen ist keine Schemaänderung erforderlich
  • positiv: Das Tabellenschema ist eng, wobei zusätzliche Zeilen für neue Einstellungen verwendet werden
  • negativ: Jede Einstellung hat den gleichen Standardwert (null / leer?)
  • negativ: alles muss als Strings gespeichert werden (dh nvarchar)
  • negativ: Wenn Sie sich mit den Einstellungen im Code befassen, müssen Sie wissen, um welchen Typ es sich bei einer Einstellung handelt, und sie umwandeln

Die Option für eine einzelne Zeile ist bei weitem die am einfachsten zu handhabende. Dies liegt daran, dass Sie jede Einstellung in der Datenbank in ihrem richtigen Typ speichern können und nicht die Typen der Einstellungen sowie deren Suchschlüssel im Code speichern müssen.

Eine Sache, mit der ich mich befasst habe, war die Verwendung mehrerer Zeilen in der "speziellen" Einzeilentabelle für einzelne Zeilen. Ich habe dies überwunden durch (in SQL Server):

  • Hinzufügen einer neuen Bitspalte mit dem Standardwert 0
  • Erstellen einer Prüfbedingung, um sicherzustellen, dass diese Spalte den Wert 0 hat
  • Erstellen einer eindeutigen Einschränkung für die Bitspalte

Dies bedeutet, dass nur eine Zeile in der Tabelle vorhanden sein kann, da die Bitspalte den Wert 0 haben muss. Aufgrund der eindeutigen Einschränkung kann jedoch nur eine Zeile mit diesem Wert vorhanden sein.

adrianbanks
quelle
5
Wir machen die einzeilige Sache in unserer LOB-Anwendung. Die Werte sind alle vom richtigen Typ, was die Verwendung in der Anwendung erheblich vereinfacht. Unser Schema wird zusammen mit der Anwendung versioniert, sodass eine Änderung des Konfigurations-Setups wie bei jeder Anwendungsrevision verwaltet wird.
DaveE
17
Einzeilig positiv: Kann in einigen Spalten FK definiert haben!
wqw
8
Sie können jederzeit ein Schlüssel / Wert-Paar mit einer Typkennung erstellen, um zu bestimmen, welche Spalte den Wert in ihrem Werttyp enthält. Dies gibt Ihnen das Beste aus beiden Welten und Sie können einen gespeicherten Prozess verwenden, um den Wert zu erhalten, wenn Sie ihn benötigen.
Middletone
19
Eine Sache, die Ihren Tag nach der Implementierung der einzeiligen Lösung wirklich ruinieren kann, ist, wenn Sie später die Aufgabe haben, "verfolgen wir auch, wann jeder Wert das letzte Mal geändert wurde und wer ihn geändert hat ..."
Dave Mateer
6
Ein weiterer Vorteil der einzeiligen Lösung, die ich in einem Fall entdeckt habe: Ich habe eine Anwendung für einen Client mit einer einzeiligen Tabelle für "Einstellungen" erstellen lassen. Später bekam ich zwei andere Clients, die dieselbe Anwendung verwenden wollten, aber unterschiedliche Einstellungen wollten: Alles, was ich tun musste, war, der Tabelle eine PK-ID "client_id" hinzuzufügen, um für jeden Client einen separaten Satz von Einstellungen zu verwalten. (Dies ist, wenn Sie erkennen, dass diese "Einstellungen" wirklich nur Attribute für eine übergeordnete Entität sind, die Sie noch nicht modelliert haben.)
Jeffrey Kemp
10

Sie sollten (zumindest) eine Tabelle mit einer Spalte für den Informationstyp und den Informationswert erstellen. Auf diese Weise müssen Sie nicht jedes Mal neue Spalten erstellen, wenn neue Informationen hinzugefügt werden.

Otávio Décio
quelle
1
Einfach und ordentlich. Arbeiten Sie einfach mit einer Liste von Schlüsselwertpaaren von dort. Vielleicht möchten Sie ein wenig über Standardwerte nachdenken, hängt vom Verwendungskontext ab ...
Paul Kohler
4
Warum ist es ein Problem, neue Spalten zu erstellen? Ich weiß, dass es Situationen gibt, in denen Entwickler dies aufgrund politischer Probleme bei der Aktualisierung von SQL-Schemata vermeiden müssen, aber dies wird in der Frage nicht erwähnt.
Finnw
6

Eine einzelne Zeile funktioniert einwandfrei. es wird sogar starke Typen haben:

show_borders    bit
admin_name      varchar(50)
max_users       int

Ein Nachteil ist, dass eine Schemaänderung ( alter table) erforderlich ist, um eine neue Einstellung hinzuzufügen. Eine Alternative ist das Normalisieren, bei dem Sie eine Tabelle wie die folgende erhalten:

pref_name       varchar(50) primary key
pref_value      varchar(50) 

Dies hat schwache Typen (alles ist ein Varchar), aber das Hinzufügen einer neuen Einstellung bedeutet nur das Hinzufügen einer Zeile, was Sie nur mit Datenbank-Schreibzugriff tun können.

Andomar
quelle
4

Persönlich würde ich es in einer einzigen Zeile speichern, wenn das funktioniert. Overkill, um es in einer SQL-Tabelle zu speichern? wahrscheinlich, aber es schadet nicht wirklich.

EJ Brennan
quelle
4

Wie Sie vermutet haben und mit Ausnahme der einfachsten Situationen, hat das Platzieren aller Konfigurationsparameter in einer einzelnen Zeile viele Nachteile. Es ist eine schlechte Idee ...

Eine bequeme Möglichkeit zum Speichern von Informationen zum Konfigurations- und / oder Benutzerpräferenztyp ist XML . Viele DBMS unterstützen den XML-Datentyp. Die XML-Syntax ermöglicht es Ihnen, die "Sprache" und Struktur zu verwenden, die die Konfiguration beschreibt, wenn sich diese Konfiguration weiterentwickelt. Ein Vorteil von XML ist die implizite Unterstützung der hierarchischen Struktur, die es beispielsweise ermöglicht, kleine Listen von Konfigurationsparametern zu speichern, ohne diese mit einem nummerierten Suffix benennen zu müssen. Ein möglicher Nachteil des XML-Formats besteht darin, dass das Suchen und allgemeine Ändern dieser Daten nicht so einfach ist wie bei anderen Ansätzen (nichts Kompliziertes, aber nicht so einfach / natürlich).

Wenn Sie näher am relationalen Modell bleiben möchten, benötigen Sie wahrscheinlich das Entity-Attribute-Value-Modell , wobei die einzelnen Werte in einer Tabelle gespeichert werden, die normalerweise wie folgt aussieht:

EntityId     (foreign key to the "owner" of this attribute)
AttributeId  (foreign key to the "metadata" table where the attribute is defined)
StringValue  (it is often convenient to have different columns of different types
IntValue      allowing to store the various attributes in a format that befits 
              them)

Wobei die AttributeId ein Fremdschlüssel für eine Tabelle ist, in der beispielsweise jedes mögliche Attribut (in Ihrem Fall "Konfigurationsparameter") definiert ist

AttributeId  (Primary Key)
Name
AttributeType     (some code  S = string, I = Int etc.)
Required          (some boolean indicating that this is required)
Some_other_fields   (for example to define in which order these attributes get displayed etc...)

Schließlich können Sie mit der EntityId eine Entität identifizieren, die diese verschiedenen Attribute "besitzt". In Ihrem Fall kann es sich um eine Benutzer-ID handeln oder nur um eine implizite, wenn Sie nur eine Konfiguration verwalten müssen.

Abgesehen davon, dass die Liste der möglichen Konfigurationsparameter mit der Entwicklung der Anwendung erweitert werden kann, platziert das EAV-Modell die "Metadaten", dh die Daten, die sich auf das Attribut selbst beziehen, in Datentabellen, wodurch die häufig verwendete harte Codierung von Spaltennamen vermieden wird wenn die Konfigurationsparameter in einer einzelnen Zeile gespeichert sind.

mjv
quelle
3
Es klingt für die meisten Verwendungen einer Konfigurationstabelle wie ein Overkill.
JerryOL
Ich finde die allgemeine Idee hinter diesem Ansatz großartig. Aber warum XML? Wählen Sie einfach ein einfaches Datenaustauschformat wie JSON oder YAML und Sie können die Vorteile der beiden anderen Varianten nutzen.
Schlamar
1
EAV ist relational, aber nicht normalisiert. Es gibt sicherlich Anwendungsfälle dafür (zum Beispiel scheinen ORM-Systeme sie zu lieben), aber das Argument, dass sich die Metadaten in der Datenbank für EAV befinden, ist kein überzeugender Grund, sie zu verwenden. Alle RDBMS enthalten ohnehin Metadaten in Systemtabellen, die Sie erkennen können. Daher enthalten Tabellen mit einer einzelnen Zeile daher auch Metadaten in der Datenbank. Fest codierte Spaltennamen sind ebenfalls kein Problem. Wenn Sie Schlüssel für Entitäten und Attribute verwenden, haben Sie an einer anderen Stelle eine fest codierte Nachschlagetabelle, die sie definiert (oder schlimmer noch, sie befindet sich in Ihrer Präsentationsebene).
Davos
3

Sie müssen Ihr Schema sicherlich nicht ändern, wenn Sie einen neuen Konfigurationsparameter im normalisierten Ansatz hinzufügen, aber Sie ändern wahrscheinlich immer noch Ihren Code, um den neuen Wert zu verarbeiten.

Das Hinzufügen einer "Änderungstabelle" zu Ihrer Bereitstellung scheint kein so großer Kompromiss für die Einfachheit und Typensicherheit des Einzelzeilenansatzes zu sein.

Dave Mikesell
quelle
2

Ein Schlüssel-Wert-Paar ähnelt einer .Net App.Config, in der Konfigurationseinstellungen gespeichert werden können.

Wenn Sie also den Wert abrufen möchten, können Sie Folgendes tun:

SELECT value FROM configurationTable
WHERE ApplicationGroup = 'myappgroup'
AND keyDescription = 'myKey';
rizalp1
quelle
1

Ein üblicher Weg, dies zu tun, besteht darin, eine "Eigenschaften" -Tabelle zu haben, die einer Eigenschaftendatei entspricht. Hier können Sie alle Ihre App-Konstanten oder nicht so konstante Dinge speichern, die Sie nur benötigen.

Sie können die Informationen dann nach Bedarf aus dieser Tabelle abrufen. Wenn Sie feststellen, dass Sie eine andere Einstellung speichern müssen, können Sie diese hinzufügen. Hier ein Beispiel:

property_entry_table

[id, scope, refId, propertyName, propertyValue, propertyType] 
1, 0, 1, "COMPANY_INFO", "Acme Tools", "ADMIN"  
2, 0, 1, "SHIPPING_ID", "12333484", "ADMIN"  
3, 0, 1, "PAYPAL_KEY", "2143123412341", "ADMIN"   
4, 0, 1, "PAYPAL_KEY", "123412341234123", "ADMIN"  
5, 0, 1, "NOTIF_PREF", "ON", "ADMIN"  
6, 0, 2, "NOTIF_PREF", "OFF", "ADMIN"   

Auf diese Weise können Sie die Daten speichern, die Sie haben, und die Daten, die Sie nächstes Jahr haben werden und von denen Sie noch nichts wissen :).

In diesem Beispiel können Ihr Gültigkeitsbereich und Ihre Referenz-ID für alles verwendet werden, was Sie im Back-End möchten. Wenn also propertyType "ADMIN" den Gültigkeitsbereich 0 refId 2 hat, wissen Sie, welche Präferenz dies ist.

Der Eigenschaftstyp kommt zum Tragen, wenn Sie eines Tages auch hier Informationen außerhalb des Administrators speichern müssen.

Beachten Sie, dass Sie Warenkorbdaten nicht auf diese Weise speichern oder nachschlagen sollten. Allerdings , wenn die Daten - System spezifisch, dann können Sie sicher diese Methode verwenden.

Beispiel: Wenn Sie Ihre DATABASE_VERSION speichern möchten, verwenden Sie eine solche Tabelle. Auf diese Weise können Sie beim Aktualisieren der App anhand der Eigenschaftentabelle feststellen, über welche Version Ihrer Software der Client verfügt.

Der Punkt ist, dass Sie dies nicht für Dinge verwenden möchten, die den Warenkorb betreffen. Halten Sie Ihre Geschäftslogik in genau definierten relationalen Tabellen. Die Eigenschaftentabelle dient nur zur Systeminfo.

Stephano
quelle
@finnw Ich stimme voll und ganz zu, dass diese Methode nicht für Suchvorgänge verwendet werden sollte, insbesondere wenn es viele verschiedene Arten von Suchvorgängen gibt. Vielleicht habe ich die Frage falsch verstanden. Es klang, als bräuchte er eine Tabelle für Konstanten und Systemeigenschaften. Warum gibt es in diesem Fall 10 verschiedene Tabellen?
Stephano
Hinweis: Er sagte "Einstellungen und Konfigurationen speichern", nicht "Ich muss relationale Warenkorbdaten speichern"
Stephano
Mein Einwand dagegen ist, dass Sie die Typisierungs- und andere Einschränkungsmechanismen von SQL umgehen, um zu vermeiden, dass das SQL-Schema aktualisiert wird, wenn Sie neue Attribute hinzufügen. Wie Sie sagen "Daten, die Sie nächstes Jahr haben werden und von denen Sie noch nichts wissen." Ja, Sie werden nächstes Jahr neue Daten haben, aber was hindert Sie daran, neue (typisierte) SQL-Spalten, CHECK- und möglicherweise FOREIGN KEY-Einschränkungen zum Zeitpunkt des Hinzufügens zu erstellen?
Finnw
Mein erster Instinkt ist, diese Daten einfach einer Flatfile hinzuzufügen. Und Sie haben Recht, dieser Prozess der Verwendung einer Tabelle umgeht tatsächlich die Einschränkungsmechanismen des DBMS. Ich würde jedoch sagen, dass Sie den Punkt verfehlen, wenn Sie sich zu sehr bemühen, die richtigen Datenbanktechniken zu befolgen. Schauen Sie sich die erste Antwort an. höchste Abstimmung über SO: stackoverflow.com/questions/406760/…
Stephano
2
Ich würde Schlüssel-Wert-Paar gehen, alles beim Start in ein Wörterbuch kopieren und sortieren.
Paul Creasey
0

Ich bin nicht sicher, ob eine einzelne Zeile die beste Implementierung für die Konfiguration ist. Möglicherweise ist es besser, eine Zeile pro Konfigurationselement mit zwei Spalten (configName, configValue) zu haben, obwohl hierfür alle Ihre Werte in Zeichenfolgen und zurück umgewandelt werden müssen.

Unabhängig davon schadet es nicht, eine einzelne Zeile für die globale Konfiguration zu verwenden. Die anderen Optionen zum Speichern in der Datenbank (globale Variablen) sind schlechter. Sie können dies steuern, indem Sie Ihre erste Konfigurationszeile einfügen und dann Einfügungen in der Tabelle deaktivieren, um mehrere Zeilen zu verhindern.

siderisch
quelle
0

Sie können das Schlüssel / Wert-Paar ohne Konvertierungen durchführen, indem Sie für jeden Haupttyp eine Spalte und eine Spalte hinzufügen, in der angegeben ist, in welcher Spalte sich die Daten befinden.

Ihr Tisch würde also ungefähr so ​​aussehen:

id, column_num, property_name, intValue, floatValue, charValue, dateValue
1, 1, weeks, 51, , ,
2, 2, pi, , 3.14159, , 
3, 4, FiscYearEnd, , , , 1/31/2015
4, 3, CompanyName, , , ACME, 

Es verbraucht etwas mehr Platz, aber Sie verwenden höchstens ein paar Dutzend Attribute. Sie können eine case-Anweisung aus dem Wert column_num verwenden, um das rechte Feld abzurufen / zu verbinden.

Spintool
quelle
0

Tut mir leid, ich komme Jahre später. Aber was ich mache, ist einfach und effektiv. Ich erstelle einfach eine Tabelle mit drei () Spalten:

ID - int (11)

Name - Varchar (64)

Wert - Text

Bevor ich eine neue Konfigurationsspalte erstelle, aktualisiere oder lese, serialisiere ich den "Wert"! Auf diese Weise bin ich mir des Typs sicher (Naja, PHP ist :))

Zum Beispiel:

b: 0; ist für B OOLEAN ( falsch )

b: 1; ist für B OOLEAN ( wahr )

i: 1988; ist für ich NT

s: 5: "Kader"; ist für einen S TRING von 5 Zeichen Länge

Ich hoffe das hilft :)

Kader Bouyakoub
quelle
1
Warum nicht einfach eine neue Spalte für den Typ erstellen? i:1988Anscheinend versuchen Sie, zwei Informationen in einer einzigen Spalte zusammenzufassen.
Maksymiuk
@maksymiuk SImply, weil einmal unserialisiert Sie den genauen Typ erhalten, anstatt eine Schleife nach (wenn oder Schalter) zu verwenden ... etc
Kader Bouyakoub
Ohne Schleifen, Schalter oder ähnliches würde der Schritt des Analysierens der Informationen aus jeder Zeile entfallen. Wenn Sie jedoch eine zusätzliche Spalte für den Typ hätten, wären die Typinformationen für die Komponente, die die Informationen abruft, bereits verfügbar ohne weitere Schritte als nur die erste Abfrage
ausführen zu müssen
Sie meinen, etwas echo (int) $varfür eine ganze Zahl und andere für andere Typen zu tun ?
Kader Bouyakoub
0

Haben Sie eine Schlüsselspalte als varchar und eine Wertespalte als JSON. 1ist numerisch, während "1"es sich um eine Zeichenfolge handelt. trueund falsesind beide boolesch. Sie können auch Objekte haben.

kzh
quelle