Du choix des types de données

Lors de mes participations au forum SQL Server de ce site, je vois souvent des membres qui manipulent des données qui ne sont pas de type chaîne de caractère dans une colonne de ce type.
Cela peut revenir à calculer la racine carrée de carottes, et constitue donc un non-sens.
Voici donc un inventaire de ce que j’ai pu rencontrer jusqu’ici, et qu’il ne faut surtout pas faire pour transformer une base de données en dépotoir de données !


=> Utiliser plusieurs colonnes de type BIT pour représenter un état

Certes cela consomme moins d’espace, mais l’atomicité du sens de la valeur est alors perdue …
Il sera donc avantageux d’utiliser une colonne de type TINYINT qui consomme seulement un octet, en laissant la porte ouverte à de nouveaux états …

=> Utiliser le type TIME pour stocker une durée

SQL Server 2008 a introduit plusieurs nouveaux types de données permettant de stocker des valeurs temporelles, notamment le type TIME, qui comme son nom l’indique, permet de stocker un horaire, et non pas une durée.
Si d’aventure nous devons stocker une durée supérieure à 24h, comment allons-nous alors procéder ? Ce choix reste acceptable si l’on est absolument certain que la durée ne dépassera jamais 24h, mais il est plus simple de stocker une durée comme un entier, ce qui permet d’utiliser très simplement la fonction DATEADD().
Une autre solution est de stocker la date de début et la date de fin de l’opération, et d’ajouter une colonne calculée qui spécifie la fonction DATEDIFF().

=> Utiliser le type DATETIME au lieu du type SMALLDATETIME

Tout aussi regrettable, la confusion entre ces deux types de données :

– DATETIME nécessite 8 octets, et permet de stocker des dates avec une précision à 3ms,
– SMALLDATETIME nécessite 4 octets, et permet de stocker des dates avec un précision à la minute.

Il est donc absurde de stocker des dates au type DATETIME pour une application qui n’en a pas besoin, comme par exemple un centre de documentation qui propose l’emprunt de livres …

Encore une fois cela a un impact important sur l’indexation, puisqu’il faudra plus de pages pour stocker la même donnée, et traverser plus de niveaux intermédiaires … il en résultera donc des lectures de pages supplémentaires et inutiles, pourrissant le cache de données, et ralentissant l’exécution des requêtes …

=> Stocker des chaînes de caractères numériques ou forcément latins dans une colonne de type Unicode (NCHAR, NVARCHAR)

Pourquoi stocker une référence, une adresse mail, un numéro de téléphone, ou un code postal en Unicode ?
Tout simplement pour consommer deux fois plus d’espace !
En effet tout caractère stocké au type de données Unicode occupe 2 octets, contre seulement 1 en CHAR ou VARCHAR.

=> Utiliser le type VARCHAR au lieu de CHAR

Si par exemple nous avons des codes qui contiennent des lettres, mais dont est certain qu’ils seront toujours représentés sur 6 caractères, alors pourquoi stocker ceux-ci dans une colonne de type VARCHAR ?
En effet le type VARCHAR fait consommer 2 octets en sus de tous les caractères de la chaîne, qui permettent de stocker l’offset de fin de cette chaîne.
Ce n’est pas le cas lorsqu’on stocke ce genre de données au type CHAR.

=> Stocker des dates dans une colonne de type CHAR | NCHAR | VARCHAR | NVARCHAR

On se sert d’une colonne de type chaîne pour stocker une date au format désiré, par exemple DDMMYYYY HH:mm:ss …
Mais il y a pire encore : stocker ces dates au format Unicode (NCHAR, NVARCHAR) surtout quand on sait que les dates ne comprennent jamais de caractères Unicode.
Rappelons donc que ces 4 types de données ne doivent servir exclusivement qu’au stockage de chaînes de caractère, et que les types Unicode permettent de stocker des caractères non latins, comme ceux des langues arabes et asiatiques.

Conséquence directe de ce choix : la perte d’intégrité des données, puisque dans une colonne de type chaîne de caractère, on peut stocker une représentation d’une date, mais aussi n’importe quelle autre chaîne de caractères.
Bien sûr, certains me répondront que l’on peut mettre une contrainte de domaine (CHECK) sur la colonne … outre le fait que la comparaison de chaînes est coûteux en ressources système, que ferez-vous en cas d’erreur ?

Autre conséquence : comment interroger la table pour retourner les données qui ont été stockées entre deux dates ? Comment utiliser les fonctions consacrées aux dates comme DATEADD(), DATEDIFF(), … ?

Enfin l’efficacité d’un index sur une colonne de type chaîne de caractère est plus connue pour être faible.

Tout cela pour ne pas avoir utilisé le type DATETIME ou SMALLDATETIME … Il faut avouer que c’est de l’auto-flagellation !

=> Utiliser le type INT au lieu de SMALLINT, TINYINT ou BIT

Voilà un précepte que l’on rencontre aussi souvent qu’il est incohérent.

Au nom de la simplicité (mais laquelle ?), nombreux sont ceux qui souhaitent stocker la valeur de clé primaire de tables de référence (jours de la semaine, quelques états possibles d’une entité (en veille, en chargement, allumé, en cours d’extinction), et j’en passe …) dans une colonne de type INT.

Alors qu’à l’évidence, il n’y aura jamais plus de 256 jours dans une semaine, stockons donc tout cela sur les 4 octets d’une colonne de type INT, au lieu de stocker la même information dans une colonne de type TINYINT, pour seulement 1 octet.
De cette façon on pourra avoir des indexes plus volumineux et moins efficaces lors des jointures, tout en pourrissant le cache de données !

=> Utiliser les types FLOAT ou REAL au lieu de NUMERIC ou DECIMAL

Les types FLOAT et REAL, réservés aux valeurs numériques imprécises, sont appropriés dans le cas où l’on a affaire à une application scientifique qui effectue des calculs en virgule flottante.

=> Utiliser les types MONEY ou SMALLMONEY au lieu de NUMERIC ou DECIMAL

Il s’agit ici plus d’une anomalie que d’autre chose, et je ne m’explique toujours pas la présence des types de données MONEY et SMALLMONEY, alors que NUMERIC et DECIMAL conviennent tout à fait au même usage.
Le pire, c’est que les premiers fonctionnent très bien pour toute opération arithmétique de base, mais entraînent des imprécisions puisqu’ils sont stockés comme des nombres à virgule flottante.
Si on doit donc utiliser des pourcentages sur des valeurs monétaires, ou utiliser des taux de change, on préfèrera les types NUMERIC ou DECIMAL, qui sont précis.

=> Le comble de la médiocrité : le « type » sql_variant

Ce pseudo-type de données est le pire qui ait été inventé : on peut tout y stocker, des chaînes de caractère, des entiers, des décimaux, un document XML, …

Voyons tous les inconvénients de ce « type » :
– Il n’est pas entièrement supporté par ODBC, qui convertit de plus toute valeur automatiquement en NVARCHAR(4000)
– On ne peut bien évidemment pas l’utiliser pour la définition d’une colonne calculée,
– On ne peut pas utiliser l’opérateur LIKE sur une colonne de ce « type »,
– Il ne peut pas être utilisé pour définir une clé primaire ou étrangère
– On ne peut pas utiliser de fonction d’aggrégation
– On ne peut pas effectuer d’opération arithmétique

Finalement tout doit être fait à l’aide des fonctions CAST() ou CONVERT(), ce qui rend bien sûr un prédicat de filtre non SARG-able

Une réflexion au sujet de « Du choix des types de données »

  1. Le choix du type des données est largement bâclé dans la plupart des petits/moyens projets et après quand on monte en charge on s’aperçoit des erreurs faites. Mieux vaut bien choisir ses types de données le plus tôt possible dans son projet.

Laisser un commentaire