web-dev-qa-db-fra.com

Meilleure conception de table pour la configuration d’application ou les paramètres d’option d’application?

J'ai besoin de stocker une série de valeurs de configuration dans une base de données. Voici deux manières de les stocker: une table avec 2 colonnes (nom, valeur) et une ligne pour chaque paire, ou une table avec une colonne pour chaque paramètre de configuration et une ligne? Avec le premier, il me suffit d'ajouter une autre ligne pour ajouter une valeur de configuration, avec le second, je dois ajouter une colonne à la table. Y a-t-il des problèmes avec l'un ou l'autre que je devrais prendre en considération? Est-ce que l'un est plus efficace que l'autre?

41
Andrew

Pour les données de configuration, j'utiliserais la structure clé/valeur avec une ligne par entrée de configuration. Vous êtes susceptible de lire ces données une fois et de les mettre en cache, les performances ne sont donc pas un problème. Comme vous l'avez souligné, l'ajout de colonnes à chaque fois que l'ensemble des clés de configuration est modifié nécessite beaucoup plus de maintenance.

SQL excelle dans la modélisation et la manipulation d'ensembles arbitrairement volumineux de données structurées de manière similaire (sinon identique). Un ensemble d’informations de configuration n’est vraiment pas cela: vous avez une seule ligne de données OR vous avez plusieurs lignes de données totalement non liées. Cela signifie que vous utilisez simplement cela comme magasin de données. Je dis ignorer le modèle de données SQL et aller simple.

22
Peter Cardona

Une autre considération: avec une colonne pour chaque paramètre de configuration, vous pouvez facilement avoir des versions. Chaque ligne représente une version.

14
Luc M

L'utilisation d'une ligne distincte pour chaque paramètre de configuration (application) (ou option d'application) présente l'inconvénient de ne pas pouvoir stocker les valeurs de paramètre dans une colonne avec un type de données approprié. Les utilisateurs peuvent-ils entrer des données avec un type type non valide? Est-ce pertinent pour votre application?

L'un des avantages de l'utilisation de colonnes séparées est que tout code de votre base de données elle-même (par exemple, procédures stockées, fonctions, etc.) peut utiliser une valeur du type de données approprié sans qu'il soit nécessaire de rechercher des valeurs non valides, puis de le convertir en type de données approprié. .

Si vous déployez manuellement les modifications dans votre base de données d'application, alors oui, si vous utilisez une conception EAV, il est very un peu plus facile de déployer de nouveaux paramètres de configuration, mais en réalité, quelles sont les économies réalisées:

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

contre:

ALTER TABLE Options ADD NewConfigurationSetting some_datatype

UPDATE Options
SET NewConfigurationSetting = NewConfigurationSettingValue
13
Kenny Evitt

La première chose à considérer est la suivante: arrêtez de penser à l’efficacité de la récupération des informations. Tout d’abord, déterminez comment efficacement et correctement modéliser les données et ensuite (et seulement ensuite) comment le faire efficacement .

Cela dépend donc de la nature des données de configuration que vous stockez. Si les paires (nom, valeur) sont fondamentalement non liées, stockez-les sous la forme d'une paire par ligne. S'ils sont liés, vous pouvez envisager un schéma comportant plusieurs colonnes.

Qu'est-ce que je veux dire par apparenté? Considérons une configuration de cache. Chaque cache a plusieurs attributs:

  • politique d'expulsion;
  • date d'expiration;
  • taille maximum.

Supposons que chaque cache a un nom. Vous pouvez stocker ces données sur trois lignes:

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

mais ces données sont liées et vous aurez souvent besoin de les récupérer toutes en même temps. Dans ce cas, il peut être judicieux d’avoir une table cache_config à cinq colonnes: id, nom, expulsion, expiration, taille_max.

C'est ce que je veux dire par données connexes.

12
cletus

Je pense que le design à 2 colonnes (nom, valeur) est bien meilleur. Comme vous l'avez dit, si vous devez ajouter une nouvelle propriété, il vous suffit de "insert" sur une nouvelle ligne. Dans l'autre conception (ligne unique), vous devez modifier le schéma de la table pour ajouter une colonne à la nouvelle propriété.

Cela dépend toutefois de si votre liste de propriétés va changer à l'avenir.

4
Aziz

Ici, je blogue sur le moment où nous déplacé notre AppSettings vers une table de base de données. La performance n’est pas un problème, car elle n’est extraite qu’une fois au début de l’application et est stockée dans un dictionnaire. recherche facile.

Pas sûr de votre application, mais la raison importante pour laquelle nous l'avons fait est qu'il est maintenant impossible d'utiliser les valeurs de production si vous êtes dans Dev, Test, etc.

4
JBrooks

J'APPUIE de mettre des valeurs non-chaînes dans une colonne-chaîne (ou types incorrects de données). (Comme @Kenny Evitt discute ci-dessus)

Donc, je viens avec le dessousalternativequi va verticalement ET traite avec les types de données corrects.

Je n'utilise pas d'argent et de petites sommes d'argent. Mais je les ai inclus par souci d’exhaustivité. Remarque, il existe quelques autres types de données. 

voir

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

Mais ci-dessous couvre la plupart des choses.

pour être honnête, je n'utilise que des chaînes (varchar (1024)), int, smallint et bit ... 99% du temps.

Ce n'est pas parfait. Aka, vous avez beaucoup de tuples nuls. Mais comme vous ne les récupérez qu'une seule fois (et en cache), le mappage sur un objet de paramétrage (en c # dans mon monde) n'est pas 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()),    

Maintenant, si c'est trop, et que vous êtes déterminé à utiliser des "chaînes" pour toutes les valeurs, voici un peu de 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 */

Maintenant, en prenant un peu plus loin, vous pouvez toujours créer des objets Dotnet fortement typés avec les types de données corrects, puis convertir votre datareader/dataset en objet fort, comme indiqué ci-dessous.

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; }
}

Vous pouvez toujours faire les classes C # (ou autre langue) ........ et utiliser la méthode SettingDataType ci-dessus. Le code "mappage" nécessite juste un peu de travail supplémentaire.

Lorsque je ne suis pas sur-voté, j'utilise les classes SettingDataType et C # comme indiqué ci-dessus.

2
granadaCoder

Vous pouvez enregistrer la configuration efficacement en utilisant XML. Certaines bases de données prennent en charge la fonctionnalité XML pur dans laquelle vous pouvez enregistrer une valeur en tant que type de données xml et vous pouvez exécuter XQUERY sur cette colonne particulière.

Créez une table avec deux noms de colonne et configuration. nom avec chaîne de type de données et configuration avec type de données xml, vous n'avez donc pas à vous soucier de l'insertion et de la suppression de nouveaux paramètres de configuration. Et si la base de données ne prend pas en charge le format XML, il suffit de l’enregistrer sous forme de chaîne mais au format XML pour pouvoir analyser cette configuration manuellement ou utiliser une API efficacement.

Je pense que ce serait une meilleure solution au lieu de stocker la configuration complète sous forme de chaîne. 

2
GG.
CREATE TABLE Configuration (
    Name ...,
    Value ...,
);

La meilleure façon. Ajouter une colonne à une table est généralement nul, et quel est l'intérêt d'une table avec une seule ligne?

Pas sûr que ce soit approprié pour SQL, mais hélas ... réponse à la question.

1
Jed Smith

J'ai utilisé les deux méthodes et je préfère la méthode des 2 colonnes. Pour revenir à la nouvelle colonne de chaque configuration, vous devez modifier le code pour ajouter de nouveaux paramètres.

Je préfère utiliser la méthode Une colonne par paramètre (lorsque j'accède à la valeur). En effet, les paramètres de configuration sont définis plus explicitement. Mais que cette préférence ne pèse pas la difficulté d'ajouter une nouvelle configuration à la table.

Je recommanderais la méthode à 2 colonnes. Configurez ensuite une fonction/sproc d’accesseur pour obtenir les valeurs.

0
Vaccano

dépend.

Si vous avez moins de dire 15 valeurs, je ferais une colonne pour chacune.

Si vous modifiez régulièrement le nombre de paramètres ou si vous n'utilisez pas souvent tous les paramètres, vous pouvez envisager de définir une ligne par paramètre.

Au-delà de cela, c'est probablement un tossup. Cela dépend de vos habitudes d'utilisation. Si vous avez toujours besoin de saisir tous les paramètres, il est probablement plus rapide de les avoir tous sur une rangée.

L'ajout de colonnes n'est pas trop difficile, et si vous programmez judicieusement, vous n'avez généralement pas à mettre à jour votre autre code.

0
JasonWoof

"Best" dépend entièrement du contexte. Comment ces données seront-elles utilisées? 

Si tout ce que vous avez à faire est de stocker et de récupérer un seul ensemble de paramètres de configuration, je voudrais tout d’abord mettre en doute l’utilisation d’une base de données relationnelle. Cela n’apporte aucun avantage évident par rapport aux fichiers de configuration du système de fichiers. Vous ne pouvez pas utiliser facilement le contrôle de version pour vos fichiers de configuration, et la gestion des différences environnementales (par exemple, les environnements "DEV", "TEST" et "PRODUCTION") nécessite à présent une interface graphique pour modifier la base de données (oh, et comment vous connecter à la base de données en premier lieu?).

Si votre application a besoin de "raisonner" sur la configuration dans son ensemble - par ex. si vous avez une solution à plusieurs locataires et que vous devez configurer l'application de manière dynamique en fonction du système actuel, nous vous suggérons de stocker les fichiers de configuration sous forme de document texte dans votre base de données, avec les métadonnées qui permettent à l'application de stocker/récupérer le document. . Différents moteurs de base de données proposent différentes solutions pour stocker des documents texte. Par exemple, dans un système multi-locataire, vous pourriez avoir:

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

Cela vous permettrait de récupérer le fichier du client 1, valable après le 3 mars, et de répondre aux besoins de l’application.

Si votre application a besoin de raisonner sur le contenu de la configuration, et non sur la configuration en tant qu'entité à part entière, le problème est différent. La solution "nom/valeur" que vous proposez est également connue sous le nom d'entité/attribut/valeur (EAV), et il y a lotsdeSOquestions discussion sur les avantages et désavantages. TL; DR: il est difficile de convertir même des questions simples en SQL avec EAV. 

Il est beaucoup plus facile d'interroger les données si chaque paramètre de configuration est une colonne, avec le type de données approprié. Mais cela signifie que vous vous retrouvez avec une table très "large" (la plupart des applications ont des dizaines voire des centaines de valeurs de configuration), et chaque fois que vous souhaitez ajouter un paramètre de configuration, vous modifiez votre schéma de base de données, ce qui n'est pas le cas. t pratique.

L’alternative consiste donc à stocker les valeurs de configuration en tant que document structuré - XML ​​et JSON sont largement pris en charge. Ces formats peuvent être interrogés par le moteur de base de données, mais ne nécessitent pas de schéma fixe.

0
Neville Kuyt