Utilizzo di una tabella di configurazione a riga singola nel database di SQL Server. Cattiva idea?


145

Nello sviluppo di un'applicazione per carrello ho scoperto che dovevo salvare le impostazioni e le configurazioni in base alle preferenze e ai requisiti dell'amministratore. Queste informazioni possono essere qualsiasi cosa, dalle informazioni sulla società, ID account di spedizione, chiavi API PayPal, preferenze di notifica, ecc.

Sembra altamente inappropriato creare una tabella per archiviare una singola riga in un sistema di database relazionale.

Qual è il modo appropriato per conservare queste informazioni?

Nota: il mio DBMS è SQL Server 2008 e il livello di programmazione è implementato con ASP.NET (in C #).

Risposte:


189

Ho fatto questo in due modi in passato - una tabella a riga singola e una tabella coppia chiave / valore - e ci sono aspetti positivi e negativi in ​​ciascun approccio.

Fila unica

  • positivo: i valori sono memorizzati nel tipo corretto
  • positivo: è più facile gestire il codice (a causa di quanto sopra)
  • positivo: è possibile assegnare valori predefiniti a ciascuna impostazione singolarmente
  • negativo: è necessaria una modifica dello schema per aggiungere una nuova impostazione
  • negativo: la tabella può diventare molto ampia se ci sono molte impostazioni

Coppia chiave / valore

  • positivo: l'aggiunta di nuove impostazioni non richiede una modifica dello schema
  • positivo: lo schema della tabella è stretto, con righe extra utilizzate per nuove impostazioni
  • negativo: ogni impostazione ha lo stesso valore predefinito (null / vuoto?)
  • negativo: tutto deve essere memorizzato come stringhe (es. nvarchar)
  • negativo: quando si hanno a che fare con le impostazioni nel codice, è necessario sapere di che tipo è un'impostazione e lanciarla

L'opzione a riga singola è di gran lunga la più semplice con cui lavorare. Questo perché è possibile memorizzare ciascuna impostazione nel tipo corretto nel database e non è necessario memorizzare i tipi di impostazioni e le relative chiavi di ricerca nel codice.

Una cosa che mi preoccupava di usare questo approccio era avere più righe nella tabella "speciale" delle impostazioni a riga singola. Ho superato questo da (in SQL Server):

  • aggiungendo una nuova colonna di bit con un valore predefinito di 0
  • creando un vincolo di controllo per assicurarsi che questa colonna abbia un valore di 0
  • creando un vincolo univoco sulla colonna di bit

Ciò significa che nella tabella può esistere solo una riga perché la colonna di bit deve avere un valore pari a 0, ma può esserci solo una riga con quel valore a causa del vincolo univoco.


5
Facciamo la cosa a fila singola nella nostra applicazione LOB. I valori sono tutti del tipo corretto, il che rende molto più semplice utilizzarli nell'applicazione. Il nostro schema è aggiornato con l'applicazione, quindi una modifica alla configurazione della configurazione viene gestita proprio come qualsiasi revisione dell'applicazione.
DaveE,

17
Singola riga positiva: è possibile definire FK su alcune colonne!
wqw,

8
Puoi sempre fare una coppia chiave / valore con un identificatore di tipo per determinare quale colonna ha il valore nel suo tipo di valore. Questo ti dà il meglio di entrambi i mondi e puoi usare un proc memorizzato per ottenere il valore quando ne hai bisogno.
Middletone,

19
Una cosa che può davvero rovinare la tua giornata dopo aver implementato la soluzione a riga singola è quando ti verrà successivamente assegnato il compito di "tenere traccia anche dell'ultima volta che è stato modificato ogni valore e chi lo ha cambiato ...."
Dave Mateer,

6
Un altro vantaggio della soluzione a riga singola, che ho scoperto in un caso: avevo un'applicazione creata per un client, con una tabella a riga singola per "impostazioni". In seguito ho ottenuto altri due client che volevano utilizzare la stessa applicazione, ma volevano impostazioni diverse: tutto quello che dovevo fare era aggiungere un PK "client_id" alla tabella per mantenere un set separato di impostazioni per ciascun client. (Questo è quando ti rendi conto che queste "impostazioni" sono in realtà solo attributi per un'entità di livello superiore che non hai ancora modellato.)
Jeffrey Kemp

10

È necessario creare una tabella con una colonna per il tipo di informazioni e il valore delle informazioni (almeno). In questo modo eviti di dover creare nuove colonne ogni volta che vengono aggiunte nuove informazioni.


1
Semplice e pulito. Lavora con un elenco di coppie chiave-valore da lì. Potresti voler pensare un po 'ai valori predefiniti, dipende dal contesto d'uso ...
Paul Kohler

4
Perché è un problema creare nuove colonne? So che ci sono situazioni in cui gli sviluppatori devono evitarlo a causa di problemi politici con l'aggiornamento degli schemi SQL, ma non si fa menzione di questo nella domanda.
Finnw,

6

Una singola riga funzionerà bene; avrà anche tipi forti:

show_borders    bit
admin_name      varchar(50)
max_users       int

Uno svantaggio è che richiede una modifica dello schema ( alter table) per aggiungere una nuova impostazione. Un'alternativa è la normalizzazione, in cui si finisce con una tabella come:

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

Questo ha tipi deboli (tutto è un varchar), ma l'aggiunta di una nuova impostazione è solo l'aggiunta di una riga, cosa che puoi fare solo con l'accesso in scrittura al database.


4

Personalmente, lo memorizzerei in una singola riga se questo è ciò che funziona. Overkill per archiviarlo in una tabella SQL? probabilmente, ma non vi è alcun danno reale nel farlo.


4

Come hai indovinato, e fatta eccezione per le situazioni più semplici, mettere tutti i parametri di configurazione in una singola riga presenta molti inconvenienti. È una cattiva idea ...

Un modo conveniente per archiviare la configurazione e / o il tipo di preferenza delle informazioni dell'utente è in XML . Molti DBMS supportano il tipo di dati XML. La sintassi XML consente di spendere il "linguaggio" e la struttura descrivendo la configurazione man mano che questa configurazione si evolve. Un vantaggio di XML è il supporto implicito per la struttura gerarchica, che consente ad esempio di memorizzare piccoli elenchi di parametri di configurazione senza doverli denominare con un suffisso numerato. Un possibile svantaggio del formato XML è che la ricerca e la modifica di questi dati non sono così semplici come altri approcci (niente di complicato, ma non così semplice / naturale)

Se vuoi rimanere più vicino al modello relazionale , il modello Entity-Attribute-Value è probabilmente quello che ti serve, per cui i singoli valori sono memorizzati in una tabella che in genere assomiglia a:

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)

Per cui AttributeId è una chiave esterna di una tabella in cui viene definito ogni possibile Attributo ("parametro di configurazione" nel tuo caso), ad esempio

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...)

Infine EntityId ti consente di identificare qualche entità che "possiede" questi vari attributi. Nel tuo caso potrebbe essere un ID utente o anche solo implicito se hai solo una configurazione da gestire.

Oltre a consentire all'elenco di possibili parametri di configurazione di crescere man mano che l'applicazione si evolve, il modello EAV inserisce i "metadati", ovvero i dati relativi all'Attributo stesso, in banche dati, evitando così tutta la codifica dei nomi di colonna comunemente visti quando i parametri di configurazione sono memorizzati in una singola riga.


3
Sembra eccessivo per la maggior parte degli usi di una tabella di configurazione.
JerryOL,

Penso che l'idea generale alla base di questo approccio sia eccezionale. Ma perché XML? Basta scegliere un semplice formato di interscambio di dati come JSON o YAML e puoi avere i vantaggi di entrambe le altre varianti.
schlamar,

1
EAV è relazionale ma non è normalizzato. Esistono certamente casi d'uso (ad esempio, i sistemi ORM sembrano adorarli), ma l'argomento secondo cui i metadati sono nel database per EAV non è un motivo convincente per usarli. Tutti i RDBMS contengono comunque metadati nelle tabelle di sistema che è possibile rilevare, quindi le tabelle a riga singola hanno quindi anche metadati nel database. Anche i nomi delle colonne codificati non sono un problema. Se usi chiavi per entità e attributi, hai una tabella di ricerca codificata da qualche altra parte che li definisce (o peggio è nel tuo livello di presentazione).
Davos,

3

Certamente non è necessario modificare lo schema quando si aggiunge un nuovo parametro di configurazione nell'approccio normalizzato, ma probabilmente si sta ancora modificando il codice per elaborare il nuovo valore.

L'aggiunta di una "tabella alter" alla distribuzione non sembra un grosso compromesso per la semplicità e la sicurezza dei tipi dell'approccio a riga singola.


2

Una coppia chiave e valore è simile a .Net App.Config che può memorizzare le impostazioni di configurazione.

Quindi, quando vuoi recuperare il valore, puoi fare:

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

1

Un modo comune per farlo è quello di avere una tabella "proprietà" simile a un file delle proprietà. Qui puoi archiviare tutte le costanti della tua app, o cose non così costanti che devi solo avere in giro.

Puoi quindi prendere le informazioni da questa tabella quando ne hai bisogno. Allo stesso modo, poiché scopri di avere qualche altra impostazione da salvare, puoi aggiungerla. Ecco un esempio:

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"   

In questo modo puoi archiviare i dati che hai e quelli che avrai l'anno prossimo e di cui non sei ancora a conoscenza :).

In questo esempio, l'ambito e il refId possono essere utilizzati per tutto ciò che si desidera sul back-end. Quindi se propertyType "ADMIN" ha un ambito 0 refId 2, sai quale preferenza è.

Il tipo di proprietà è disponibile quando, un giorno, è necessario memorizzare anche informazioni non amministrative.

Tieni presente che non dovresti archiviare i dati del carrello in questo modo o le ricerche per quella materia. Tuttavia, se i dati sono specifici del sistema , è possibile utilizzare questo metodo.

Ad esempio: se desideri archiviare DATABASE_VERSION , utilizzeresti una tabella come questa. In questo modo, quando devi aggiornare l'app, puoi controllare la tabella delle proprietà per vedere quale versione del tuo software ha il client.

Il punto è che non vuoi usarlo per cose che riguardano il carrello. Mantieni la tua logica aziendale in tabelle relazionali ben definite. La tabella delle proprietà è solo per le informazioni di sistema.


@finnw Sono pienamente d'accordo sul fatto che questo metodo non dovrebbe essere usato per le ricerche, specialmente quando ci sono molti tipi diversi di ricerche. Forse ho frainteso la domanda. Sembrava che avesse bisogno di una tabella per le costanti e le proprietà del sistema. In tal caso, perché avere 10 tabelle diverse?
Stephano,

nota: ha detto "salva impostazioni e configurazioni", non "Ho bisogno di salvare i dati del carrello relazionale"
Stephano

La mia obiezione a questo è che stai bypassando la tipizzazione di SQL e altri meccanismi di vincolo per evitare di aggiornare lo schema SQL quando aggiungi nuovi attributi. Come dici "dati che avrai l'anno prossimo e di cui non sei ancora a conoscenza". Sì, avrai nuovi dati l'anno prossimo, ma cosa ti impedirà di creare nuove colonne SQL (tipizzate), CHECK e possibilmente i vincoli FOREIGN KEY al momento dell'aggiunta?
Finnw,

Il mio primo istinto è semplicemente aggiungere questi dati a un file flat. E hai ragione, questo processo di utilizzo di una tabella invece eluderà i meccanismi di vincolo del DBMS. Tuttavia, direi che se ti sforzi troppo per seguire le tecniche di database appropriate, ti manca il punto. Dai un'occhiata alla prima risposta; il più votato su SO: stackoverflow.com/questions/406760/…
Stephano

2
Vorrei andare coppia chiave-valore, scaricare tutto in un dizionario all'avvio e il tuo ordinato.
Paul Creasey,

0

Non sono sicuro che una singola riga sia la migliore implementazione per la configurazione. Potrebbe essere meglio avere una riga per elemento di configurazione con due colonne (configName, configValue), anche se ciò richiederà il cast di tutti i valori su stringhe e ritorno.

Indipendentemente da ciò, non c'è nulla di male nell'usare una singola riga per la configurazione globale. Le altre opzioni per memorizzarlo nel DB (variabili globali) sono peggiori. È possibile controllarlo inserendo la prima riga di configurazione, quindi disabilitando gli inserimenti nella tabella per impedire più righe.


0

Puoi fare la coppia chiave / valore senza conversioni aggiungendo una colonna per ogni tipo principale e una colonna che ti dice in quale colonna si trovano i dati.

Quindi il tuo tavolo sarebbe simile a:

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, 

Usa un po 'più di spazio ma al massimo stai usando alcune dozzine di attributi. È possibile utilizzare un'istruzione case dal valore column_num per estrarre / unire il campo destro.


0

Scusa, vengo come, anni dopo. Ma comunque, quello che faccio è semplice ed efficace. Creo semplicemente una tabella con tre () colonne:

ID - int (11)

nome - varchar (64)

valore - testo

Quello che faccio prima di creare una nuova colonna di configurazione, aggiornarla o leggere è serializzare il "valore"! In questo modo sono sicuro del tipo (beh, php è :))

Per esempio:

b: 0; è per B OOLEAN ( falso )

b: 1; è per B OOLEAN ( vero )

i: 1988; è per I NT

s: 5: "Kader"; è per una S TRING di 5 caratteri

Spero che aiuti :)


1
Perché non creare semplicemente una nuova colonna per il tipo? i:1988sembra che tu stia cercando di comprimere due informazioni in una singola colonna.
Maksymiuk,

@maksymiuk SImply perché una volta non serializzato ottieni il tipo esatto invece di usare un loop dopo (if o switch) ... ecc.
Kader Bouyakoub,

non è necessario alcun loop o switch o altro, in realtà rimuoverebbe il passaggio di dover analizzare le informazioni da ogni riga, mentre, se si avesse una colonna aggiuntiva per il tipo, le informazioni sul tipo sono già disponibili per il componente che estrae le informazioni senza dover fare ulteriori passi oltre alla semplice query iniziale
maksymiuk,

Intendi fare qualcosa come echo (int) $varun numero intero e altri per altri tipi?
Kader Bouyakoub,

0

Hanno una colonna chiave come varchar e una colonna valore come JSON. 1è numerico mentre "1"è una stringa. truee falsesono entrambi booleani. Puoi avere anche oggetti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.