Changez rapidement la colonne NULL en NOT NULL

11

J'ai une table avec des millions de lignes et une colonne qui autorise les valeurs NULL. Cependant, aucune ligne n'a actuellement une valeur NULL pour cette colonne (je peux le vérifier assez rapidement avec une requête). Cependant, lorsque j'exécute la commande

ALTER TABLE MyTable ALTER COLUMN MyColumn BIGINT NOT NULL;

la requête prend une éternité relative. Cela prend en fait entre 10 et 20 minutes, plus de deux fois plus longtemps que l'ajout d'une contrainte de vérification. Existe-t-il un moyen de mettre à jour instantanément les métadonnées de la table pour cette colonne, d'autant plus que je sais qu'aucune ligne n'a de valeur NULL pour cette colonne?

Joseph Daigle
la source
2
Non (ou du moins n'utilisant pas de méthodes documentées / prises en charge). Voir Pourquoi ALTER COLUMN to NOT NULL provoque une croissance massive des fichiers journaux?
Martin Smith
2
Il peut également être en attente d'un Sch-Mverrou lorsque cela prend "pour toujours". Avez-vous regardé pour voir s'il était en attente ou occupé?
Martin Smith
@MartinSmith J'ai clarifié ce que je veux dire pour toujours . Je teste cela dans un environnement de développement sans qu'aucune autre session n'atteigne la base de données. Il se termine finalement. Mais la réponse que vous avez liée explique pourquoi cela prend si longtemps. Si vous pouvez reformuler votre commentaire en réponse, je l'accepterai.
Joseph Daigle

Réponses:

12

La réponse de @ ypercube gère cela partiellement en tant que changement de métadonnées uniquement.

L'ajout de la contrainte NOCHECKsignifie qu'aucune ligne ne devra être lue pour la vérifier, et si vous partez d'une position où la colonne ne contient pas de NULLvaleurs (et si vous savez qu'aucune ne sera ajoutée entre la vérification et l'ajout de la contrainte), alors, comme la contrainte empêche la NULLcréation de valeurs à partir de futures INSERTou d' UPDATEopérations, cela fonctionnera.

L'ajout de la contrainte peut cependant avoir un impact sur les transactions simultanées. Le ALTER TABLEdevra d'abord acquérir une Sch-Mserrure. Pendant qu'il attend cela, tous les autres accès à la table seront bloqués comme décrit ici .

Une fois le Sch-Mverrou acquis, l'opération devrait cependant être assez rapide.

Un problème avec cela est que même si vous savez que la colonne n'a pas de NULLs, la contrainte n'est pas approuvée par l'optimiseur de requête, ce qui signifie que les plans peuvent être sous-optimaux.

CREATE TABLE T (X INT NULL)

INSERT INTO T 
SELECT ROW_NUMBER() OVER (ORDER BY @@SPID)
FROM master..spt_values

ALTER TABLE T WITH NOCHECK
  ADD  CONSTRAINT X_NOT_NULL 
    CHECK (X IS NOT NULL) ; 

SELECT *
FROM T 
WHERE X NOT IN (SELECT X FROM T)

Plan

Comparez cela avec le plus simple

ALTER TABLE T ALTER COLUMN X INT NOT NULL

SELECT *
FROM T 
WHERE X NOT IN (SELECT X FROM T)

Plan

Un problème possible que vous pourriez rencontrer avec la modification de la définition de colonne de cette manière est qu'il doit non seulement lire toutes les lignes pour vérifier qu'elles remplissent la condition, mais peut également finir par effectuer des mises à jour journalisées des lignes .

Une solution intermédiaire pourrait être d'ajouter la contrainte de vérification WITH CHECK. Cela sera plus lent que WITH NOCHECKnécessaire pour lire toutes les lignes, mais cela permet à l'optimiseur de requête de donner le plan le plus simple dans la requête ci-dessus et il devrait éviter le problème de mises à jour enregistrées possibles.

Martin Smith
la source
7

Vous pouvez, au lieu de modifier la colonne, ajouter une CHECKcontrainte de table avec l' NOCHECKoption:

ALTER TABLE MyTable WITH NOCHECK
  ADD  CONSTRAINT MyColumn_NOT_NULL 
    CHECK (MyColumn IS NOT NULL) ;
ypercubeᵀᴹ
la source
1
Cela empêcherait les futures mises à jour ou insertions qui font la colonne NULLmais ne pourraient pas être utilisées par l'optimiseur de requête.
Martin Smith
@MartinSmith Oh oui, je viens de lire la réponse et les commentaires dans la question similaire: Comment ajouter une colonne NOT NULL à une grande table dans SQL Server? Veuillez ajouter une réponse aux problèmes ou une meilleure solution et je supprimerai la mienne.
ypercubeᵀᴹ
2
Je n'ai pas de meilleure solution. J'ai voté en faveur de cela parce qu'il fournit une solution partielle. Si tout ce que l'OP veut faire est d'empêcher les données invalides, cela fonctionnera (et devrait être plus rapide que ALTER COLUMNcomme une fois le Sch-Mverrou acquis, cela n'a pas besoin de balayer les lignes du tout). Je souligne simplement que ce n'est pas tout à fait la même chose (par exemple, s'il est utilisé dans une NOT INrequête, le plan sera plus complexe)
Martin Smith