MySQL : activer/désactiver une contrainte d'unicité sur certains tuples

Dans une table de votre base de données MySQL, vous souhaitez vérifier qu'une valeur est unique, mais seulement dans certains cas. Méthode.

La situation n'est pas courante, mais elle peut arriver : dans une table de votre base de données, vous souhaitez vérifier qu'une valeur est unique, mais seulement dans certains cas. Il existe un moyen plus simple que de tordre son modèle pour obtenir ce résultat. La solution n'est pas miraculeuse mais a le mérite d'être assez simple à mettre en œuvre.

Imaginons un modèle un peu bateau :

- une table de véhicules, dont certains n'acceptent qu'un occupant, d'autres plusieurs,
- une table d'usagers, tous présents dans un et un seul véhicule.

On obtient un schéma semblable à celui-ci :

- (table) véhicule :
         o id_véhicule (clé primaire)
         o nom_véhicule
         o ...
- (table) usager :
         o id_usager (clé primaire)
         o id_véhicule (clé étrangère)
         o nom_usager
         o ...

Il est impossible, avec ce schéma, de vérifier si un véhicule ne devant accueillir qu'un seul usager ne dépasse pas sa capacité : il faut vérifier que l'identifiant de ce véhicule (id_véhicule) ne soit présent qu'une fois dans la table. Ajouter une contrainte d'unicité sur notre table usager interdirait de fait d'avoir des véhicules accueillant plusieurs usagers.

On peut aussi ajouter un champ id_usager, qui serait une clé étrangère dans notre table véhicule, qui ne serait renseignée que pour les véhicules monoplaces. Mais on se retrouve à devoir vérifier dans les deux tables (véhicule et usager) pour obtenir notre ou nos usager(s).

Conditionner l'activation de la contrainte d'unicité à la valeur d'un champ

L'autre solution que j'ai rencontrée avec Magento (et plus précisément dans la gestion des règles de promotion associées à un ou plusieurs coupons de réduction) consiste à conditionner l'activation de la contrainte d'unicité à la valeur d'un champ. La méthode est simple : dans notre cas, on placera la contrainte d'unicité sur le champ id_vehicule et un nouveau champ qu'on appellera exclusif. Si la valeur de ce champ exclusif est NULL, alors l'absence de valeur rend impossible de tester la l'unicité des valeurs de ce couple. Si la valeur de ce champ est donnée (il faut qu'elle soit unique, j'y reviens plus bas) alors la contrainte d'unicité est activée et l'identifiant du véhicule est contrôlé.

Cette méthode permet d'obtenir la liste des utilisateurs en effectuant la requête sur la seule table usager, et c'est là son principal intérêt : on obtient des meilleurs performances à la lecture. En contrepartie, le schéma de la base conserve une valeur de plus.

J'ai quelques doutes sur l'intérêt de cette méthode : pour moi il s'agit essentiellement d'une manière de modifier les contraintes métier d'un système associé à un schéma de base de données existant. D'ailleurs, dans la mesure ou notre champ exclusif pourra souvent accueillir plusieurs valeurs, la contrainte d'unicité n'est que partielle. Dans Magento, le champ est de type tinyint(1), il est donc possible d'associer 10 "usagers" à un véhicule supposément réservé à un seul.

Contenu réalisé par Martin Richard (Martius Web) sous licence Creative Commons.