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

21

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 peut être nvarchar OU il peut s'agir d'une clé étrangère vers une table Unités distincte qui pourrait faciliter le calcul des conversions d'unités. Par contre, l'ajout de de nombreuses colonnes pourraient 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, alors je vais aller de l'avant et répondre à ma propre question. (Voir ci-dessous)

kmote
la source
La meilleure pratique consiste à avoir un système de mesure unique utilisé de manière universelle et cohérente tout au long de l'application. SI serait le système de choix. Les valeurs dans d'autres systèmes seront converties pendant le chargement ou dans la couche de présentation, où chaque utilisateur peut choisir son ensemble préféré.
Michael Green

Réponses:

12

Puisque vous mentionnez des centaines de colonnes, je considérerais une 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 casting que Joe décrit 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 pourriez aller plus loin et mettre en œuvre un modèle de style "modèle de données universel" basé sur cet article de David Hay et également décrit dans son livre Data Model Patterns: Conventions of Thought. 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 indiqué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.

Todd Everett
la source
Merci pour cette réflexion très intéressante - j'ai beaucoup appris. Cependant, je ne pense pas que l'EAV soit le modèle approprié dans mon cas (si je comprends bien votre suggestion) car, bien que nous ayons des centaines de colonnes, elles ne sont en aucun cas rares. Cependant, cette DID a suscité une idée connexe (voir MISE À JOUR dans mon message d'origine).
kmote
Votre idée me semble assez bonne - je ne peux pas penser à part d'autre problème que celui que vous avez déjà signalé. Mais si les colonnes peuvent être renommées / modifiées, ce serait un problème dans n'importe quelle conception. C'est à ce moment que la collaboration est amusante - une idée émerge à laquelle aucun de nous n'a pensé au départ!
Todd Everett
8

Tout le travail.

Notez que dans le deuxième cas, vous ne pouvez pas ajouter de pommes et d'oranges, et donc les données sont exceptionnellement faciles à faire l'objet d'une mauvaise interprétation.

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

Cade Roux
la source
1
La «mauvaise interprétation» potentielle que vous mentionnez est exactement l'une des préoccupations que j'ai concernant l'architecture actuelle de cette base de données - et quelque chose que j'essaie de trouver un moyen de réduire.
kmote
1
grand point sur l'inconvénient potentiel de la solution de nom de colonne.
kmote
1
@kmote Ce n'est pas un problème simple - nous avons des rapports où les transactions individuelles peuvent avoir différentes unités de mesure d'origine, mais il y a aussi un total - qui est un total après conversion en une unité sélectionnée par l'utilisateur.
Cade Roux
7

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

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

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

dodgy_coder
la source
5

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

peroyhav
la source
3

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. Ce que je pense que je vais faire est de créer une séparée ColumnUnitstable, avec quatre colonnes: Schema, Table, Column, UnitsID(où UnitsID est FK à un séparé UnitsOfMeasuretableau), la cartographie 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 souvenir de modifier cette table chaque fois qu'ils renomment une colonne ou une table [ peut-être utiliser un déclencheur DDL ? ], sinon le système se 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.

kmote
la source
puzzle intéressant ... et idée intéressante que vous avez. votre idée faciliterait la recherche, mais ne semble pas faire grand-chose. vous venez de déplacer les données de référence vers un autre endroit. ce qui me dérange le plus dans ce design
Sir Swears-a-lot
... c'est que si un élément a plus d'attributs, vous devez toujours ajouter plus de colonnes. pour cette raison, j'aime la suggestion de @todd everett d'un design eav.
Sir Swears-a-lot