web-dev-qa-db-fra.com

Meilleure façon de stocker des unités dans la base de données

J'ai hérité d'une grande base de données (SQLServer) avec des centaines de colonnes qui représentent des quantités d'une chose ou d'une autre. Les unités de ces valeurs (par exemple "gallons", "pouces", etc.) sont stockées dans le champ MS_Description des propriétés étendues. Je me demande s'il existe une meilleure façon de stocker ces informations. Je suppose que c'est bien à des fins de documentation, mais il serait difficile de faire des calculs de conversion d'unité robustes sur la base de ces données. À ce stade, je ne suis pas prêt à effectuer un changement invasif, mais si j'en ai l'occasion, quelle est la meilleure pratique recommandée à cet égard? Les options, du haut de ma tête, pourraient inclure:

  • Changez le nom de la colonne en unités incluses (par exemple, "TotalVolumeInGallons". Cela rendrait les informations un peu plus facilement disponibles, mais elles me semblent encore faibles.)
  • Ajoutez une colonne "Unités" distincte pour correspondre à chaque colonne "Montant" (cette colonne pourrait être nvarchar OR il pourrait s'agir d'une clé étrangère vers une table Unités distincte qui pourrait faciliter le calcul de l'unité) D'un autre côté, ajouter autant de colonnes pourrait doubler la taille de ma base de données - avec des données terriblement redondantes.)
  • Créez un nouveau champ dans les propriétés étendues dédié spécifiquement aux unités. (Malheureusement, je ne pense pas que cela puisse être une clé étrangère vers une table d'unités.)
  • Y a-t-il une autre idée que j'écarte?

MISE À JOUR: Après avoir lu la réponse de @Todd Everett, une solution possible m'est venue, donc je vais aller de l'avant et répondre à ma propre question. (Voir ci-dessous)

21
kmote

Après avoir lu la réponse de @Todd Everett, une solution m'est venue à l'esprit, alors je vais continuer et répondre à ma propre question. Je pense que je vais créer une table ColumnUnits distincte, avec quatre colonnes: Schema, Table, Column, UnitsID (où UnitsID est FK dans une table UnitsOfMeasure distincte), mappant ainsi une colonne donnée à son unité de mesure associée. De toute évidence, le plus gros inconvénient de cette idée est que les développeurs devraient se rappeler de modifier cette table chaque fois qu'ils renomme une colonne ou une table [ peut-être utiliser un déclencheur DDL ? ], sinon le système cassera . Mais en supposant que de tels renommages sont rares et que le dev-shop soit petit (une seule personne, dans mon cas), cette architecture devrait être réalisable. L'avantage est qu'aucune modification invasive ne doit être apportée à la base de données actuelle, et je n'ai à stocker la valeur qu'une seule fois pour chaque colonne, plutôt qu'une fois par ligne, comme ma deuxième option dans mon message d'origine l'exigerait.

3
kmote

Puisque vous mentionnez des centaines de colonnes, je considérerais un conception EAV . Alors que Joe Celko met en garde contre cela , je pense que cela peut être applicable dans votre cas d'utilisation. Il semble que tous vos "montants" soient des nombres, vous éviterez ainsi les problèmes de transtypage décrits par Joe et la nécessité de faire de chaque "valeur" une chaîne. Cela fonctionnera encore mieux si tous les montants sont des nombres entiers, mais peut également fonctionner si certains sont décimaux. Compte tenu des unités de mesure, vous pouvez aller plus loin et implémenter un modèle de style "modèle de données universel" basé sur cet article par David Hay et également décrit dans son livre Modèles de modèle de données: Conventions de pensée . Ce modèle a l'avantage supplémentaire de configurer quels "montants" s'appliquent à quelles "choses" si vous en avez besoin. Une étape supplémentaire illustrée dans le livre à la page 162 est un tableau de conversion d'unité de mesure que vous pouvez utiliser pour convertir entre les différentes unités de mesure. Voici un exemple:

UOM Conversion              

UOM From    UOM To        Cal Step  Operator Factor Constant
Kilograms   Pounds        1         *        2.2
Celsius     Fahrenheit    1         *        1.8
Celsius     Fahrenheit    2         +               32

Cela signifie que pour convertir de Kg en Lb, la première étape consiste à multiplier Kg par 2,2. Il existe également une constante si une conversion doit également inclure une valeur constante et la possibilité de créer plusieurs étapes. Ainsi, lors de la conversion, dites Celsius en Fahrenheit, vous multipliez Celsius par 1,8, puis ajoutez 32. La clé serait le de UOM, le à UOM et l'étape de calcul.

C'est ma valeur de 2 cents. J'espère que ces références vous donneront matière à réflexion si jamais vous avez la possibilité de redémarrer le design actuel.

12
Todd Everett

Tout le travail.

Notez que dans le second cas, vous ne pouvez pas ajouter de pommes et d'oranges, et donc les données sont exceptionnellement faciles à être mal interprétées.

Notez également que les conversions ne peuvent pas être très sûres et sont susceptibles d'erreurs d'arrondi, de débordements, etc.

De plus, il y a des problèmes physiques comme la gravité spécifique et la température. Pour convertir 20 gallons d'eau en livres, il vous faudrait connaître la densité de l'eau. Mais la densité de l'eau change avec la température, vous devrez donc peut-être connaître la densité contemporaine à la mesure ou la température de manière similaire et utiliser un facteur de correction de volume.

Dans le cas des propriétés étendues, ce n'est bon que pour la documentation - un bon nom de colonne est préférable pour la documentation. Le problème avec la colonne implicite comme étant dans une unité fixe par nom est que vous finissez par vous mettre dans un coin lorsque vous changez d'unités de mesure - un nouveau client veut du pétrole en barils et non en gallons - et ce serait bien puisque leurs données sont en sa propre base de données, mais le nom de la colonne est désormais trompeur.

Une autre option consiste à stocker les versions canoniques dans des unités fixes (c'est-à-dire toujours des kilogrammes et des mètres) en plus des mesures originales variables. Les opérations d'agrégation sur les unités fixes devraient être correctes (sauf que vous n'ajouteriez pas de températures, par exemple), mais vous ne perdez pas la mesure d'origine.

8
Cade Roux

Une solution simple qui a bien fonctionné pour moi dans le passé consiste à stocker toutes vos données dans les unités "de base". Par exemple, votre unité de base pour les longueurs peut être en millimètres et votre unité de base pour les poids peut être en kilogrammes. Cette solution peut entraîner la nécessité de convertir certaines de vos données existantes dans l'unité de base, si ce n'est pas déjà fait.

Une fois que vous avez toutes les données dans les unités de base standard, il n'est pas nécessaire de stocker l'unité dans la base de données elle-même, car il s'agit désormais d'une hypothèse à l'échelle du système. Les unités affichées requises pour chaque type d'unité (par exemple, s'il faut afficher mm, pouces, cm, m pour la longueur) deviennent un problème de domaine d'application/client, qui peut être enregistré sur le stockage local.

Les tables de conversion d'unité pour la conversion entre les différentes unités prises en charge peuvent être codées en dur dans votre application, car les nouvelles unités de mesure changent extrêmement rarement.

N.B. une solution connexe à un autre problème est que lors du stockage d'horodatages dans une base de données toujours les stocker dans l'unité "de base" - UTC .

Un autre Q&A connexe sur le sujet ...

7
dodgy_coder

Étant donné que toute unité peut être convertie en une autre unité du même type Avec la formule:

y = ((x + xOffset) * multiplicand / denominator) + yOffset

Je créerais une table qui contient les types d'unités plus ces 4 valeurs.

From Unit     To Unit      Unit Type    From Offset    Multiplicand    Denominator    To Offset
'milligrams'  'grams'      'mass'       0              1               1000           0
'grams'      'kilograms'   'mass'       0              1               1000           0
'grams'      'ounces'      'mass'       0              100000          2835           0
'ounces'     'pound'       'mass'       0              1               16             0

Après avoir ajouté toutes les mesures que vous êtes susceptibles de convertir vers et à partir de chaque côté de la liste, exécutez une requête dans laquelle vous insérez l'opération inverse en annulant simplement les décalages et en échangeant le multiplicande et le dénominateur et les unités À et À partir de l'unité.

Pour ajouter une conversion entre tous les types, une jointure croisée Avec certains filtres peut insérer les conversions restantes.

5
peroyhav