2009-09-07 9 views
34

Ho bisogno di memorizzare una serie di valori di configurazione in un database. Un paio di modi in cui ho pensato di memorizzarli sono: una tabella con 2 colonne (nome, valore) e una riga per ogni coppia, o una tabella con una colonna per ogni parametro di configurazione e 1 riga? Con il primo ho solo bisogno di aggiungere un'altra riga per aggiungere un valore di configurazione, con il secondo ho bisogno di aggiungere una colonna alla tabella. Ci sono dei problemi con i quali dovrei prendere in considerazione? Uno è più efficiente dell'altro?Miglior design della tabella per la configurazione dell'applicazione o le impostazioni delle opzioni dell'applicazione?

risposta

18

Per i dati di configurazione, utilizzare la struttura chiave/valore con una riga per voce di configurazione. È probabile che tu legga questi dati una volta e li metti in cache, quindi le prestazioni non rappresentano un problema. Come si fa notare, l'aggiunta di colonne ogni volta che il set di chiavi di configurazione cambia richiede molta più manutenzione.

SQL eccelle nel modellare e manipolare serie arbitrariamente grandi di dati strutturati allo stesso modo (se non lo stesso). Un insieme di informazioni di configurazione non è proprio questo: hai una singola riga di dati O hai più righe di dati completamente indipendenti. Questo dice che lo stai usando solo come archivio dati. Dico saltare il modello di dati SQL e andare semplice.

+1

+1 per la memorizzazione nella cache. – APC

3

Penso che il design a 2 colonne (nome, valore) sia molto meglio. Come hai detto, se devi aggiungere una nuova proprietà, tutto ciò che devi fare è "insert" una nuova riga. Mentre nell'altra progettazione (riga singola), è necessario modificare lo schema della tabella per aggiungere una colonna per la nuova proprietà.

Questo, tuttavia, dipende dal fatto che l'elenco di proprietà cambierà in futuro.

+3

Penso che intendessi il design a 2 colonne, non "2-row". –

+0

Devo confessare che la possibilità di aggiungere una nuova proprietà non dovrebbe essere così difficile anche se è memorizzata in una colonna separata. Non stai migrando manualmente le modifiche allo schema del DB per la tua applicazione negli ambienti di produzione, vero? –

0
CREATE TABLE Configuration (
    Name ..., 
    Value ..., 
); 

Il modo migliore. L'aggiunta di una colonna a una tabella di solito fa schifo, e qual è il punto di una tabella con una riga?

Non sicuro che questo sia appropriato per SQL, ma ahimè ... risposta alla domanda.

10

Il primo problema da considerare è questo: smettere di pensare all'efficienza del recupero delle informazioni. prima di tutto, capire come efficacemente e correttamente modello di dati e quindi (e solo allora) a capire come farlo in modo efficiente .

Quindi dipende dalla natura dei dati di configurazione che si stanno memorizzando. Se le coppie separate (nome, valore) non sono sostanzialmente correlate, memorizzarle come una per riga. Se sono correlati, puoi prendere in considerazione uno schema con più colonne.

Cosa intendo per correlato? Prendi in considerazione alcune configurazioni della cache. Ogni cache ha diversi attributi:

  • politica di sgombero;
  • tempo di scadenza;
  • dimensione massima.

Assumere che ogni cache abbia un nome. È possibile memorizzare questi dati come tre file:

  • <name>_EVICTION
  • <name>_EXPIRY
  • <name>_MAX_SIZE

ma questo dato è relativi e spesso potrebbe essere necessario per recuperare tutti in una volta. In tal caso potrebbe essere logico avere una tabella cache_config con cinque colonne: id, name, eviction, expiry, max_size.

Questo è ciò che intendo per dati correlati.

0

Ho usato entrambi i metodi e preferisco il metodo a 2 colonne. Il richiamo alla nuova colonna per ogni configurazione è che è necessario modificare il codice per aggiungere nuove impostazioni.

Preferisco utilizzare la colonna One per metodo di impostazione (quando accedo al valore). Questo perché le impostazioni di configurazione sono impostate in modo più esplicito. Ma questa preferenza non appesantisce la difficoltà di aggiungere una nuova configurazione alla tabella.

Suggerirei il metodo a 2 colonne. Quindi impostare una funzione accessor/sproc per ottenere i valori.

14

Un'altra considerazione: con una colonna per ogni parametro di configurazione, è possibile avere facilmente versioni. Ogni riga rappresenta una versione.

+1

Questa è un'idea molto interessante! –

0

dipende.

Se hai meno di 15 valori, creo una colonna per ciascuno.

Se si modifica il numero di impostazioni regolarmente, o se spesso non si utilizzano tutte le impostazioni, prenderei in considerazione la creazione di una riga per impostazione.

Oltre a ciò, è probabilmente un sballottamento. Dipende dai tuoi schemi di utilizzo. Se hai sempre bisogno di prendere tutte le impostazioni, probabilmente è più veloce averle in una riga.

L'aggiunta di colonne non è troppo difficile e, se si programma in modo ragionevole, di solito non è necessario aggiornare alcun altro codice.

2

È possibile salvare la configurazione in modo efficiente tramite XML. Alcuni database supportano funzionalità Pure XML in cui è possibile salvare valore come tipo di dati xml ed è possibile eseguire XQUERY su quella particolare colonna.

Creare una tabella con due colonne nome e configurazione. nome con tipo di dati stringa e configurazione con tipo di dati xml quindi non c'è bisogno di preoccuparsi di inserimento e cancellazione dei nuovi parametri di configurazione, sarà solo un nuovo tag in xml. E se il database non supporta l'XML, salvalo come una stringa ma in formato XML in modo da poter analizzare la configurazione manualmente o utilizzare alcune API in modo efficiente.

Penso che questo sarebbe un modo migliore invece di memorizzare la configurazione completa come una stringa.

2

Qui ho un blog su quando abbiamo moved our AppSettings to a Database Table. La prestazione non è un problema perché è pull una sola volta all'inizio dell'applicazione e memorizzato in un dizionario per una facile ricerca.

Non sono sicuro circa la vostra applicazione, ma la ragione importante per cui abbiamo fatto questo è ora è impossibile essere utilizzando i valori di produzione se si è in Dev, Test, ecc

11

Uno svantaggio di usare una riga separata per ogni impostazione di configurazione (applicazione) (o applicazione) è impossibile memorizzare i valori di impostazione in una colonna con un tipo di dati appropriato. Gli utenti possono inserire dati con un valore valido? È pertinente alla tua applicazione?

Uno dei vantaggi dell'utilizzo di colonne separate è che qualsiasi codice nel DB stesso (ad es. Stored procedure, funzioni, ecc.) Può utilizzare un valore del tipo di dati appropriato senza dover prima verificare i valori non validi e quindi convertire in il tipo di dati appropriato.

Se si sta distribuendo manualmente modifiche alla vostra applicazione DB, allora sì se si sta utilizzando un design EAV E 'molto leggermente più facile da implementare nuove impostazioni di configurazione, ma in realtà qual è il risparmio per:

INSERT Options (ConfigurationSetting, Value) 
VALUES ('NewConfigurationSetting', NewConfigurationSettingValue) 

versus:

ALTER TABLE Options ADD NewConfigurationSetting some_datatype 

UPDATE Options 
SET NewConfigurationSetting = NewConfigurationSettingValue 
1

io disprezzo mettendo valori non stringa in una stringa colonne (aka, errato-tipi di dati). (Come @Kenny Evitt discute sopra)

Così sto venire con la sottostante alternativa che va in verticale e si occupa di tipi di dati corretti.

In realtà non uso soldi e smallmoney. Ma li ho inclusi per completezza. Nota, ci sono un paio di altri tipi di dati là fuori

vedere

https://msdn.microsoft.com/en-us/library/ms187752.aspx?f=255&MSPPError=-2147217396

Ma la maggior parte delle cose al di sotto copre.

a dire il vero, io uso solo la stringa (varchar (1024)), int, smallint e bit ... il 99% delle volte.

Non è perfetto. Aka, hai un sacco di tuple null. Ma dal momento che li prendi solo una volta (e cache), la mappatura di un oggetto impostazioni (in C# nel mio mondo) non è difficile.

CREATE TABLE [dbo].[SystemSetting](
[SystemSettingId] [int] IDENTITY NOT NULL, 

[SettingKeyName] [nvarchar](64) NOT NULL, 
[SettingDataType] [nvarchar](64) NOT NULL, /* store the datatype as string here */ 

[SettingValueBigInt] bigint NULL, 
[SettingValueNumeric] numeric NULL, 
[SettingValueSmallInt] smallint NULL, 
[SettingValueDecimal] decimal NULL, 
[SettingValueSmallMoney] smallmoney NULL, 
[SettingValueInt] int NULL, 
[SettingValueTinyInt] tinyint NULL, 
[SettingValueMoney] money NULL, 
[SettingValueFloat] float NULL, 
[SettingValueReal] real NULL, 
[SettingValueDate] date NULL, 
[SettingValueDateTimeOffSet] datetimeoffset NULL, 
[SettingValueDateTime2] datetime2 NULL, 
[SettingValueSmallDateTime] smalldatetime NULL, 
[SettingValueDateTime] datetime NULL, 
[SettingValueTime] time NULL, 
[SettingValueVarChar] varchar(1024) NULL, 
[SettingValueChar] char NULL, 

[InsertDate] [datetime] NOT NULL DEFAULT (GETDATE()),    
[InsertedBy] [nvarchar](50) NOT NULL DEFAULT (SUSER_SNAME()),  
[LastUpdated] [datetime] NOT NULL DEFAULT (GETDATE()),    
[LastUpdatedBy] [nvarchar](50) NOT NULL DEFAULT (SUSER_SNAME()),  

)

Ora, se questo è troppo, e siete determinati a usare "stringhe" per tutti i valori, quindi ecco alcune DDL.

DROP TABLE [dbo].[SystemSetting] 
DROP TABLE [dbo].[SystemSettingCategory] 

CREATE TABLE [dbo].[SystemSettingCategory] (
    [SystemSettingCategoryId] [int] NOT NULL, 
    [SystemSettingCategoryName] [nvarchar](64) NOT NULL, 
    [InsertDate] [datetime] NOT NULL DEFAULT (GETDATE()),    
    [InsertedBy] [nvarchar](50) NOT NULL DEFAULT (SUSER_SNAME()),  
    [LastUpdated] [datetime] NOT NULL DEFAULT (GETDATE()),    
    [LastUpdatedBy] [nvarchar](50) NOT NULL DEFAULT (SUSER_SNAME()),  
    CONSTRAINT [PK_SystemSettingCategory] PRIMARY KEY CLUSTERED ([SystemSettingCategoryId] ASC), 
    CONSTRAINT UQ_SystemSettingCategoryName UNIQUE NONCLUSTERED ([SystemSettingCategoryName]) 
) 




CREATE TABLE [dbo].[SystemSetting] (
    [SystemSettingId] [int] NOT NULL, 
    [SystemSettingCategoryId] INT NOT NULL,  /* FK to [SystemSettingCategory], not shown here */ 
    [SettingKeyName] [nvarchar](64) NOT NULL, 
    [SettingValue] nvarchar(1024) NULL, 
    [InsertDate] [datetime] NOT NULL DEFAULT (GETDATE()),    
    [InsertedBy] [nvarchar](50) NOT NULL DEFAULT (SUSER_SNAME()),  
    [LastUpdated] [datetime] NOT NULL DEFAULT (GETDATE()),    
    [LastUpdatedBy] [nvarchar](50) NOT NULL DEFAULT (SUSER_SNAME()),  
    CONSTRAINT [PK_SystemSetting] PRIMARY KEY CLUSTERED ([SystemSettingId] ASC), 
    CONSTRAINT FK_SystemSettingCategory_SystemSettingCategoryId foreign key ([SystemSettingCategoryId]) references [SystemSettingCategory] ([SystemSettingCategoryId]), 
    CONSTRAINT UQ_SystemSettingCategoryId_SettingKeyName UNIQUE NONCLUSTERED ([SystemSettingCategoryId] , [SettingKeyName]) 
) 



INSERT INTO [dbo].[SystemSettingCategory] ([SystemSettingCategoryId] , [SystemSettingCategoryName]) 
select 101 , 'EmployeeSettings' UNION ALL select 201, 'StopLightSettings' 

INSERT INTO [dbo].[SystemSetting] ([SystemSettingId] , [SystemSettingCategoryId] , [SettingKeyName] , [SettingValue]) 
      select 1001 , 101 , 'MininumAgeRequirementMonths' , convert(varchar(16) , (12 * 18)) 
UNION ALL select 1002 , 101 , 'MininumExperienceMonths' , convert(varchar(8) , 24) 
UNION ALL select 2001 , 201 , 'RedLightPosition' , 'top' 
UNION ALL select 2002 , 201 , 'YellowLightPosition' , 'middle' 
UNION ALL select 2003 , 201 , 'GreenLightPosition' , 'bottom' 

/* should fail */ 
/* start 
INSERT INTO [dbo].[SystemSettingCategory] ([SystemSettingCategoryId] , [SystemSettingCategoryName]) 
select 3333 , 'EmployeeSettings' 
INSERT INTO [dbo].[SystemSettingCategory] ([SystemSettingCategoryId] , [SystemSettingCategoryName]) 
select 101 , 'xxxxxxxxxxxxxx' 
INSERT INTO [dbo].[SystemSetting] ([SystemSettingId] , [SystemSettingCategoryId] , [SettingKeyName] , [SettingValue]) 
      select 5555 , 101 , 'MininumAgeRequirementMonths' , 555 
INSERT INTO [dbo].[SystemSetting] ([SystemSettingId] , [SystemSettingCategoryId] , [SettingKeyName] , [SettingValue]) 
      select 1001 , 101 , 'yyyyyyyyyyyyyy' , 777 
INSERT INTO [dbo].[SystemSetting] ([SystemSettingId] , [SystemSettingCategoryId] , [SettingKeyName] , [SettingValue]) 
      select 5555 , 555 , 'Bad FK' , 555 
end */ 


Select * from [dbo].[SystemSetting] where [SystemSettingCategoryId] = 101 /* employee related */ 
Select * from [dbo].[SystemSetting] where [SystemSettingCategoryId] = 201 /* StopLightSettings related */ 

Ora, prendendo un grande più lontano, è comunque possibile creare oggetti dotnet fortemente tipizzati con i tipi di dati corretti, e quindi convertire il vostro datareader/set di dati in un oggetto forte come si vede qui sotto.

public class EmployeeSettings 
{ 
    public Int16 MininumAgeRequirementMonths { get; set; } 
    public Int16 MininumExperienceMonths{ get; set; } 
} 


public class StopLightSettings 
{ 
    public string RedLightPosition { get; set; } 
    public string YellowLightPosition { get; set; } 
    public string GreenLightPosition { get; set; } 
} 

si può ancora fare le classi C# (o qualsiasi lingua) ........ e utilizzare il metodo SettingDataType sopra. Il codice "mappatura" ha solo bisogno di un piccolo lavoro extra.

Quando non è in uscita, utilizzo le classi SettingDataType e C# come sopra.

0

"Il migliore" dipende interamente dal contesto: come verranno utilizzati questi dati?

Se tutto ciò che è necessario è archiviare e recuperare un singolo set di impostazioni di configurazione, metterei in dubbio l'uso di un database relazionale in primo luogo - non aggiunge alcun vantaggio evidente sui file di configurazione sul file system. Non è possibile utilizzare facilmente il controllo della versione per i file di configurazione e gestire le differenze ambientali (ad es."DEV", "TEST" e "PRODUCTION" ambienti) ora richiede una GUI per modificare il database (oh, e come ci si connette al database in primo luogo?).

Se l'applicazione deve "ragionare" sulla configurazione nel suo insieme - ad es. se si dispone di una soluzione multi-tenant e occorre configurare l'applicazione in modo dinamico in base al sistema corrente, suggerirei di archiviare i file di configurazione come documento di testo nel database, con i metadati che consentono all'applicazione di archiviare/recuperare il documento . Diversi motori di database hanno diverse soluzioni per la memorizzazione di documenti di testo. Per esempio, in un sistema multi-tenancy si potrebbe avere:

ID client_id valid_from  valid_until configuration_file 
------------------------------------------------------- 
1   1 2016/03/16   NULL  <<DOCUMENT>> 

questo permetterebbe di recuperare il file per il cliente 1, che era valido dopo il 3 marzo e fare tutto ciò che le esigenze applicative.

Se l'applicazione deve ragionare sul contenuto della configurazione, non sulla configurazione come entità a sé stante, si presenta un problema diverso. La soluzione "nome/valore" che proponi è conosciuta anche come Entità/Attributo/Valore (EAV) e ci sono lotsofSOquestions discutendo vantaggi e svantaggi. TL; DR: è difficile convertire anche semplici domande in SQL quando si usa EAV.

È molto più facile interrogare i dati se ogni impostazione di configurazione è una colonna, con il tipo di dati appropriato. Ma questo significa che si finisce con una tabella molto "ampia" (la maggior parte delle applicazioni ha decine o addirittura centinaia di valori di configurazione) e ogni volta che si desidera aggiungere un'impostazione di configurazione, si finisce per modificare lo schema del database, che non è t pratico.

L'alternativa, quindi, è quella di memorizzare i valori di configurazione come un documento strutturato: XML e JSON sono ampiamente supportati. Questi formati possono essere interrogati dal motore del database, ma non richiedono uno schema fisso.

Problemi correlati