Dati di configurazione: tabella a riga singola vs. tabella nome-valore-coppia


64

Supponiamo che tu scriva un'applicazione che può essere configurata dall'utente. Per memorizzare questi "dati di configurazione" in un database, vengono comunemente utilizzati due modelli.

  1. La tabella a riga singola

      CompanyName  |  StartFullScreen  |  RefreshSeconds  |  ...
    ---------------+-------------------+------------------+--------
      ACME Inc.    |        true       |       20         |  ...
    
  2. La tabella nome-valore-coppia

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

Ho visto entrambe le opzioni in natura ed entrambe hanno evidenti vantaggi e svantaggi, ad esempio:

  • Le tabelle a riga singola limitano il numero di opzioni di configurazione che è possibile avere (poiché il numero di colonne in una riga è generalmente limitato). Ogni opzione di configurazione aggiuntiva richiede una modifica dello schema DB.
  • In una tabella nome-valore-coppia tutto è "tipizzato in modo stringente" (devi codificare / decodificare i tuoi parametri booleani / data / ecc.).
  • (molti altri)

Esiste un consenso all'interno della comunità di sviluppo su quale opzione sia preferibile?


2
Non c'è motivo per cui l'approccio "verticale" non possa avere tipi di dati diversi. Aggiungi una colonna int, float e text per riga. Salva / carica valori da esso usando funzioni specifiche del tipo, come 'SaveConfigInt (' field ', n)'
GrandmasterB

4
C'è un'eccellente domanda StackOverflow che chiede questo, e la risposta migliore offre vantaggi e svantaggi di entrambi gli approcci. stackoverflow.com/questions/2300356/…
Kevin - Ripristina Monica il

1
Approccio 3: colonna singola / riga singola con un formato di scambio dati semplice come JSON o YAML. Combina i vantaggi di entrambi gli approcci.
schlamar,

che dire dell'utilizzo della tabella a riga singola con dati complessi contenenti xml / json come <config> <CompanyName> ACME Inc. </CompanyName> <StartFullScreen> true </StartFullScreen>20<RefreshSeconds></RefreshSeconds> </config> e convalidare l'oggetto nel livello aziendale?
Giovanni,

1
@Giovanni: buona idea, se sono necessarie strutture gerarchiche. In caso contrario, è solo l'opzione 2 con maggiore complessità.
Heinzi,

Risposte:


15

Personalmente preferisco le tabelle a riga singola per la maggior parte delle cose. Mentre è vero che è meno flessibile, a meno che non ti aspetti un comportamento dinamico, è perfettamente accettabile aggiungere ulteriori colonne in seguito, se necessario. In un certo senso, è l'equivalente dell'uso di un dizionario / mappa per contenere coppie nome-valore rispetto all'avere membri della classe durante la programmazione. Certo, non è una metafora perfetta, ma molti dei vantaggi e degli svantaggi sono paralleli quando ci pensi.

Quindi useresti un dizionario / mappa sui membri della classe? Probabilmente no a meno che tu non abbia motivo di pensare che la quantità di dati da rappresentare sia completamente adattabile, proprio come avere una tabella di coppie nome-valore.


Cosa succede se i dati da archiviare sono definiti dall'utente? vale a dire pensare a un'interfaccia utente in cui l'utente può creare un "campo" specificando l'etichetta del campo, il tipo di dati che contiene ecc. Ciò significherebbe eseguire le istruzioni DDL dal codice. Andresti ancora con l'opzione 1?
Devanalista

1
@devanalyst No, se i dati potessero cambiare da componente a componente, non avrebbe senso tentare di creare una tabella statica per rappresentarlo. In tal caso sarebbe meglio usare la seconda opzione.
Neil,

12

Generalmente andrei con l'opzione 2 MA avrei più colonne per imporre il tipo di dati

ConfigOption   |   textValue    |   DateValue   |   NumericValue

L'opzione 1 ha il vantaggio aggiuntivo di poter facilmente "scambiare" intere configurazioni aggiungendo una Activecolonna.


Se stai per consentire la disabilitazione delle configurazioni (per l'opzione 1), almeno impostale come un activatedOntimestamp, in modo da poter sapere quando è stato attivato. E se stai andando con l'opzione 2 ... cosa succede se la fine memorizza valori in più colonne (o è un oracolo, dove (apparentemente) null e una stringa vuota sono equivalenti)?
Clockwork-Muse

1
@ X-Zero, la memorizzazione di più configurazioni viene solitamente eseguita a scopo di test, ma un timestamp non può far male. The Config Maintenance, chiama per ottenere il valore saprebbe quale colonna controllare, se davvero lo volessi, potresti aggiungere una colonna per il tipo di dati .. Ma penso che sia finita uccidere ...
Morons

5
uno schema EATV (Entity-Attribute-Type-Value) rompe la terza forma normale; la colonna Tipo è solo indirettamente correlata alla chiave primaria della tabella, attraverso la colonna Valore descritta dalla colonna Tipo. Inoltre, l'archiviazione e l'istanza di tipo dinamico non risolvono molto; se un metodo GetConfigValue () può restituire qualsiasi tipo, deve restituire Object (o ricevere in qualche modo il tipo previsto) che deve comunque essere valutato in fase di esecuzione.
KeithS

5
Ogni volta che l'opzione 1 è stata implementata nel software che ho visto, doveva essere convertita in opzione 2. L'opzione 2 è più facile da mantenere nel tempo, basta un tocco in più per implementarla correttamente la prima volta. L'opzione 1 è rapida e facile da implementare ma la manutenzione nel tempo è orribile a meno che il tuo software non sia minuscolo e senza possibilità di crescita.
Jimmy Hoffa,

8

Per me, se vai a fila singola o EAV dipende da come vuoi consumarli.

Il potere di EAV è che è possibile aggiungere nuovi dati senza modificare la struttura. Ciò significa che se si desidera un nuovo valore di configurazione, è sufficiente aggiungerlo alla tabella ed estrarlo nel punto desiderato nel codice e non è necessario aggiungere un nuovo campo al dominio, schema, mappatura, query DAL , eccetera.

Il suo difetto è che ha solo la struttura più debole, che richiede di gestire i dati pessimisticamente. Ogni utilizzo di qualsiasi valore di configurazione deve aspettarsi che il valore non sia presente o non sia nel formato corretto e comportarsi di conseguenza quando non lo è. Un valore di configurazione potrebbe non essere analizzabile in un double, in un int o in un char. Potrebbe essere nullo. potrebbe non esserci alcuna riga per il valore. I modi per aggirare questo in genere richiedono l'esistenza di un singolo valore "predefinito" valido per tutti i valori di configurazione di un particolare tipo di codice ( estremamente raro; più spesso il valore predefinito è altrettanto problematico per il consumo del codice come nessuno), oppure per mantenere un dizionario hardcoded di valori predefiniti (che deve cambiare ogni volta che viene aggiunta una nuova colonna, rendendo piuttosto discutibile il vantaggio principale dell'archiviazione EAV).

Una singola riga larga è praticamente l'opposto. Lo si mappa su una singola istanza di un oggetto Configuration con un campo / proprietà per ogni valore di configurazione esistente. Sai esattamente quale tipo dovrebbero essere quei valori in fase di compilazione e "DAL non riesci velocemente" nel DAL se non esiste una colonna di configurazione o non ha un valore del tipo corretto, dandoti un posto dove catturare le eccezioni in base su problemi di recupero / idratazione della configurazione.

Lo svantaggio principale è che è necessario un cambiamento strutturale per ogni nuovo valore; nuova colonna DB, nuova colonna nel DAL (mappatura o query SQL / SP), nuova colonna dominio, tutto necessario per testare correttamente l'utilizzo.

La situazione corretta in cui utilizzare uno di questi è la situazione in cui gli svantaggi sono mitigati. Per me, la maggior parte delle situazioni per la configurazione del codice ha richiesto un'implementazione a riga singola. Questo principalmente perché se stai introducendo un valore di configurazione completamente nuovo che regola il comportamento di alcune parti del tuo programma, devi già modificare il codice per utilizzare il nuovo valore di configurazione; perché non passare all'oggetto config e aggiungere il valore da utilizzare?

In breve, uno schema EAV per l'archiviazione della configurazione non risolve davvero il problema che pretende di risolvere e la maggior parte delle soluzioni alternative ai problemi che presenta violano il DRY.


3

In particolare per i valori di configurazione, direi: vai con la riga singola. A meno che tu non stia attualmente attraversando uno sviluppo, con che frequenza cambieranno comunque quelle colonne?

Probabilmente è meglio proteggere il tipo di dati dei valori , piuttosto che il codice per l'estensibilità che non si avrà probabilmente nei tempi di inattività tra rilasci di grandi dimensioni (r). Inoltre, aggiungere o rimuovere una singola colonna è la migrazione più semplice che ci sia. Non prevedo un mal di testa quando si crea una nuova opzione di configurazione.

Inoltre, hai detto che "utenti" possono configurare queste opzioni, senza dare un limite. Sono configurazioni per utente? In tal caso, sosterrò ancora più fortemente che le opzioni di configurazione dovrebbero essere nelle colonne - una singola riga per utente. Salverà molti mal di testa di manutenzione in seguito.


2

Se i tuoi clienti possono elaborare frammenti JSON (che non sono solo matrici e dizionari, ma anche stringhe semplici, numeri, valori booleani, valori null), puoi avere una tabella multi-riga con nome dell'opzione e un valore stringa contenente JSON. Ciò consente di memorizzare anche valori strutturati e il codice per l'elaborazione di questi dovrebbe essere già presente.

Se i tuoi client non sono in grado di elaborare frammenti JSON, ottieni nuovi client.


1

Pro a riga singola: ben definito. Contro: cambiare la configurazione può essere una seccatura. Migrazioni di DB ecc.

Entity-Value Pro: super flessibile, supporta l'evoluzione della tua configurazione. Contro: integrità referenziale? Ulteriori controlli nel codice per vedere se la proprietà esiste prima di poter fare qualcosa su di essa.

Vorrei adottare l'approccio 2 supportato da un db non relazionale come Mongo. Se c'è qualcosa di cui puoi essere sicuro, il suo cambiamento.


1

Utilizza entrambi!

Ordina quali opzioni possono avere più istanze e quali opzioni sono generiche.

La tabella a riga singola (configurazioni)

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

La tabella nome-valore-coppia (opzioni)

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

Penso che questo sia più flessibile.

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.