Choisir dans MySQL entre le type ENUM et une table externe avec une jointure

La plupart des utilisateurs ne connaissent pas ou peu les avantages et les inconvénients du Type ENUM proposé par la base de données MySQL. Résultat:  par peur de l’inconnu, les développeurs n’utilisent tout simplement pas cette fonctionnalité qui permet pourtant une amélioration significative des performances (dans certains cas)  sans  perdre en simplicité. Opposé à la contrainte CHECK connu par nos amis ORACLE, son utilisation et son fonctionnement est très différent, et ces 2 fonctionnalités ne font tout simplement pas la même chose. Cet article devrait vous permettre de savoir dans quel cas il est intéressant d’utiliser le type ENUM, et surtout dans quel cas ne pas utiliser le type ENUM.

Avant de commencer, il convient de rappeler de quoi on parle, le type ENUM est  un type de colonne proposé par la base MySQL pour y stocker une chaîne de caractère. Contrairement à une chaîne classique (VARCHAR) où on peut stocker n’importe quoi, le nombre de possibilité est limité dès la conception et nécessite un modification de la structure de la table (ALTER) pour pouvoir autoriser de nouvelles valeurs.  Cette solution implique une liste de valeurs possibles figée ou du moins très peu souvent mise à jour.

Contrairement à la fonction CHECK qui permet de restreinte les valeurs possibles (Attention, la fonction CHECK fait beaucoup d’autres choses aussi ;-)), le type ENUM ne se contente pas de simplement vérifier. Le nombre de valeur étant limité, MySQL stocke également l’information sous la forme d’une entier, ce qui diminue non seulement la quantité de données à stocker, mais peut aussi améliorer les performances lors de la lecture sur de très grosse table. Le stockage numérique se fait sur le principe que le type SMALLINT, c’est d’ailleurs pour ça que l’énumération est limitée à 65535 éléments. Attention, des spécificités particulières concernant les chaines NULL et vide sont à connaitre et disponibles sur le manuel de référence de MySQL.

Sans le type ENUM, l’autre solution serait des créer une seconde table contenant l’ensemble des valeurs possibles avec une clé primaire numérique et ensuite de faire une FOREIGN KEY vers la clé primaire et de vérifier à chaque insertion l’existence dans la seconde . La première table contiendrait alors des entiers (1,2,3,4) obligeant à aller voir à quoi correspondent les valeurs dans l’autre table. L’avantage de l’ENUM est donc de proposer cette information d’un seul coup d’oeil dans n’importe quel client SQL sans perdre en performance lors du traitement de vos données.

Voilà donc pour la théorie, mais maintenant dans quel cas utiliser le type ENUM et dans quel cas y préférer la JOINTURE?

Pour la démonstration, j’ai essayé de prendre quelques exemples concret trouvés sur des forums lors de mes recherches. (Site du Zéro – Forum – Quel est est le problème avec Enum par exemple)

Civilité des clients

Le premier exemple le plus évident est celui qui consiste à stocker la civilité des clients.

Dans cet exemple, le nombre de choix est donc assez limité :

  • Monsieur (M)
  • Madame (Mme)
  • Mademoiselle (Mlle)

Le choix du type ENUM parait très adapté à ce cas là, en effet, le nombre de choix ne devrait pas trop changer dans le temps et permet de faire des requêtes très simples sans jointure.

Voici pour les différents requêtes SQL:

CREATE TABLE  `clients` (
`nom` VARCHAR( 50 ) NOT NULL ,
`prenom` VARCHAR( 50 ) NOT NULL ,
`civilite` ENUM(  'Monsieur',  'Madame',  'Mademoiselle' ) NOT NULL
) ENGINE = MYISAM ;
INSERT INTO `clients` (`nom`, `prenom`, `civilite`) VALUES ('Dupont', 'Jean', 'Monsieur');
INSERT INTO `clients` (`nom`, `prenom`, `civilite`) VALUES ('Doe', 'Johanna', 'Madame');

Dans votre formulaire de création de client, la selectbox sera certainement fixe et codée en dur (non dynamique), même si vous souhaitez ajouter des civilités plus rares du type:

  • Docteur
  • Professeur
  • Seigneur
  • Duc
  • Baron
  • Autres titres de civilité (voir Wikipedia)

Même ces quelques civilités supplémentaires peuvent facilement être ajoutées à votre ENUM, à condition bien sûr que vous n’ayez qu’une seule selectbox à un seul endroit dans votre application et que vous pouvez facilement la modifier.

Attention, pour les applications multi-langues, il est possible que le type ENUM ne soit adapté, ‘Monsieur’ ne veut pas vraiment dire grand chose pour un développeur anglais.

Vrai, Faux, y’a t-il une vie après le boolean?

Combien de fois vous avez utilisez des booleans dans vos bases de données, dans ce cas, il n’y a pas de problème, tout le monde utilise 0 pour faux et 1 pour vrai, mais combien de fois avez-vous eu besoin de gérer un troisième cas sans pour autant vouloir systématiquement créée une nouvelle table?

  • l’activité d’un compte : Inactif/Actif  et le cas Bloqué
  • Le profil d’un utilisateur : Normal/ Gestionnaire /Admin
  • Le statut d’un ticket dans un outil de tracking (OUVERT,EN-COURS,FERME)

L’utilisateur du type ENUM ou d’une jointure dépend ici très fortement des évolutions possibles de votre application et de sa conception.

Pour le cas de l’activité ou non d’un compte, les 3 cas possibles seront probablement prévu à la conception, et vous aurez certainement un switch case pour gérer précisément les 3 cas. Même si vous deviez ajouter un quatrième cas un jour, il est fort probable que vous repassiez par une phase de conception et de développement d’une nouvelle version de l’application pour gérer ce nouveau cas.  Dans ce cas, un script d’update de base est courant et ajouter ce cas au type ENUM dans MySQL ne devrait donc pas poser de problème.

ALTER TABLE `user` CHANGE `user` `etat` ENUM('Actif','Inactif','Bloque','En-attente');

Pour le deuxième cas, vous pourriez très bien le gérer également avec un ENUM. La seule différence, c’est que dans bien des cas, la gestion des droits d’une application se fait à l’aide d’une liste de droits associée aux différents profils. Dans ce cas, il vous serait alors impossible d’utiliser le type ENUM. Il existe plusieurs raisons à cela:

  • Tout simplement parce que si vous n’avez pas de liste de profils dans une table séparée, vous ne pourrez pas y associer des droits (avec une contrainte de clé étrangère)
  • La plupart des applications de ce type charge les droits en fonction du profil associé à l’utilisateur. Même si les droits sont fixes car contrôlés en dur dans l’application, il n’est pas rare que les profils soient eux dynamiques, et qu’un utilisateur puisse en ajouter. Dans ce cas, il est hors de question de faire un ALTER TABLE de la table à chaque ajout/suppression de profil.

Dans le dernier exemple où un ticket peut avoir différents statuts, c’est plus ou moins la même chose, tout dépend du nombre de statuts différents, et du traitement fait par l’application. Si les statuts sont fixes, et ne changent jamais (Votre application est prévu pour gérer 3 statuts et c’est tout), il n’y a aucun problème à utiliser le type ENUM. En revanche, si votre application de tickets propose une gestion dynamique des statuts (ajout/édition/suppression), c’est le cas de la plupart des bugs tracker (flyspray,etc), il vous faut alors impérativement préférer la solution d’une table séparé avec une clés étrangère et des jointures le cas échéant dans vos requêtes.

Enum vs Jointure, le mot de la fin

Pour conclure, contrairement à ce que certains disent, le type ENUM n’a pas vraiment de défaut à proprement parlé, il répond à un besoin réel et adapté à certains cas d’utilisation et vient en complément de la solution traditionnel d’une jointure sur une table externe. La plupart des inconvénients cités sur les forums correspondent finalement à une mauvaise utilisation de cette fonctionnalité.