Même si l’on essaie toujours de viser juste du premier coup, un modèle de données n’est jamais complet à la première implémentation, et il peut notamment arriver que l’on ait besoin de rajouter certaines colonnes à des tables déjà remplies. Et lorsqu’il s’agit de colonnes non Nullables et que l’on souhaite formaliser la modification sous forme de script, quelques bonnes pratiques sont à connaître.
Lorsque l’on bricole un peu dans son coin et que l’on cherche à ajouter une colonne à une table existante, on ne s’embête en général pas trop et on utilise l’interface de SQL Server Management Studio (SSMS). Mais dans un développement professionnel, tout se fait par script, afin de pouvoir être rejouer à l’identique sur chaque environnement (tests, production, …).
Plaçons-nous par exemple sur SQL Server 200R2, sur la base d’exemple AventureWorks2008R2, que l’on peut trouver ici : http://msftdbprodsamples.codeplex.com/
Supposons que l’on cherche à rajouter une colonne ModifiedDate, non nullable, dans la table DatabaseLog.
ALTER TABLE dbo.DatabaseLog ADD ModifiedDate datetime NOT NULL
Le problème est que cet ajout n’est pas autorisé en l’état :
Msg 4901, Niveau 16, État 1, Ligne 1
ALTER TABLE permet d’ajouter uniquement des colonnes pouvant contenir des valeurs NULL, des colonnes ayant une définition DEFAULT, des colonnes identité ou de type timestamp ; si aucune des conditions précédentes n’est satisfaite, la table doit être vide pour autoriser l’ajout de cette colonne. La colonne ‘ModifiedDate’ ne peut pas être ajoutée à une table non vide ‘DatabaseLog’ car elle ne répond pas à ces conditions.
ALTER TABLE permet d’ajouter uniquement des colonnes pouvant contenir des valeurs NULL, des colonnes ayant une définition DEFAULT, des colonnes identité ou de type timestamp ; si aucune des conditions précédentes n’est satisfaite, la table doit être vide pour autoriser l’ajout de cette colonne. La colonne ‘ModifiedDate’ ne peut pas être ajoutée à une table non vide ‘DatabaseLog’ car elle ne répond pas à ces conditions.
Effectivement, c’est tout à faire compréhensible. Quelle serait la valeur de cette colonne pour les enregistrements existants ?
Choisissons donc par exemple de mettre 01/01/2001 en valeur par défaut.
ALTER TABLE dbo.DatabaseLog ADD ModifiedDate datetime NOT NULL DEFAULT '20010101'
Première étape, OK. Mais maintenant, il faudrait peut-être revenir à notre objectif, c’est-à-dire de créer une colonne non nullable. Il faut donc supprimer la « contrainte » DEFAULT que nous avons mise en place.
On s’aperçoit que le système a automatiquement fixé un nom pour la contrainte (commençant par DF, puisqu’il s’agit d’une contrainte DEFAULT, et contenant le début du nom de la table et du nom de la colonne). Et ce nom se termine par un identifiant aléatoire, qui ici nous pose bien problème. En effet, nous avons pour objectif de supprimer cette contrainte, et cela devient problématique si le nom est aléatoire…
En fait, l’erreur vient du script via lequel nous avons ajouté la colonne. C’était le moment (ou jamais) de fixer un nom pour la colonne.
ALTER TABLE dbo.DatabaseLog ADD ModifiedDate datetime NOT NULL CONSTRAINT DF_DatabaseLog_ModifiedDate DEFAULT '20010101'
Maintenant, il devient beaucoup plus facile de supprimer la contrainte, puisque nous en connaissons le nom.
ALTER TABLE dbo.DatabaseLog DROP CONSTRAINT DF_DatabaseLog_ModifiedDate
Donc voici la petite astuce : lorsque l’on rajoute une contrainte, il convient de toujours la nommer, afin de pouvoir la contrôler par la suite. Cette bonne pratique est aussi vraie pour les autres contraintes (clés primaires, Check, …)