Changer une colonne de NOT NULL à NULL - Que se passe-t-il sous le capot?

25

Nous avons un tableau avec 2,3 milliards de lignes. Nous aimerions changer une colonne de NOT NULL à NULL. La colonne est contenue dans un index (pas l'index cluster ou PK). Le type de données ne change pas (c'est un INT). Juste la nullité. La déclaration est la suivante:

Alter Table dbo.Workflow Alter Column LineId Int NULL

L'opération prend plus de 10 avant de l'arrêter (nous ne l'avons même pas encore laissée se terminer car c'est une opération de blocage et prenait trop de temps). Nous allons probablement copier la table sur un serveur de développement et tester le temps qu'il faut réellement. Mais, je suis curieux de savoir si quelqu'un sait ce que SQL Server fait sous le capot lors de la conversion de NOT NULL en NULL? De plus, les index affectés devront-ils être reconstruits? Le plan de requête généré n'indique pas ce qui se passe.

La table en question est groupée (pas un tas).

Randy Minder
la source
2
Je pense qu'il faudrait mettre à jour le bitmap nul sur toutes les pages de données de niveau feuille. Et avec 2,3 milliards de lignes, je parie qu'il y aurait beaucoup de pages à parcourir. Mais je n'en suis pas trop sûr.
souplex
3
Peut également être occupé à placer un bitmap nul sur l'index. Le bitmap NULL ne sera PAS présent dans un INDEX NON CLUSTERED si toutes les colonnes faisant partie de la définition d'index sont définies comme NOT NULL.
souplex

Réponses:

27

Comme mentionné par @Souplex dans les commentaires, une explication possible pourrait être si cette colonne est la première NULLcolonne de table de l'index non cluster auquel elle participe.

Pour la configuration suivante

CREATE TABLE Foo
  (
     A UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID() PRIMARY KEY,
     B CHAR(1) NOT NULL DEFAULT 'B'
  )

CREATE NONCLUSTERED INDEX ix
  ON Foo(B);

INSERT INTO Foo
            (B)
SELECT TOP 100000 'B'
FROM   master..spt_values v1,
       master..spt_values v2 

sys.dm_db_index_physical_stats montre que l'index non clusterisé ixa 248 pages feuilles et une seule page racine.

Une ligne typique dans une page feuille d'index ressemble à

entrez la description de l'image ici

Et dans la page racine

entrez la description de l'image ici

Puis en cours d'exécution ...

CHECKPOINT;

GO

ALTER TABLE Foo ALTER COLUMN B  CHAR(1) NULL;


SELECT Operation, 
       Context,
       ROUND(SUM([Log Record Length]) / 1024.0,1) AS [Log KB],
       COUNT(*) as [OperationCount]
FROM sys.fn_dblog(NULL,NULL)
WHERE AllocUnitName = 'dbo.Foo.ix'
GROUP BY Operation, Context

Revenu

+-----------------+--------------------+-------------+----------------+
|    Operation    |      Context       |   Log KB    | OperationCount |
+-----------------+--------------------+-------------+----------------+
| LOP_SET_BITS    | LCX_GAM            | 4.200000    |             69 |
| LOP_FORMAT_PAGE | LCX_IAM            | 0.100000    |              1 |
| LOP_SET_BITS    | LCX_IAM            | 4.200000    |             69 |
| LOP_FORMAT_PAGE | LCX_INDEX_INTERIOR | 8.700000    |              3 |
| LOP_FORMAT_PAGE | LCX_INDEX_LEAF     | 2296.200000 |            285 |
| LOP_MODIFY_ROW  | LCX_PFS            | 16.300000   |            189 |
+-----------------+--------------------+-------------+----------------+

En vérifiant à nouveau la feuille d'index, les lignes ressemblent maintenant à

entrez la description de l'image ici

et les lignes dans les pages de niveau supérieur comme ci-dessous.

entrez la description de l'image ici

Chaque ligne a été mise à jour et contient désormais deux octets pour le nombre de colonnes ainsi qu'un autre octet pour NULL_BITMAP.

En raison de la largeur de ligne supplémentaire, l'index non cluster a maintenant 285 pages feuilles et maintenant deux pages de niveau intermédiaire avec la page racine.

Le plan d'exécution du

 ALTER TABLE Foo ALTER COLUMN B  CHAR(1) NULL;

ressemble à ceci

entrez la description de l'image ici

Cela crée une toute nouvelle copie de l'index plutôt que de mettre à jour l'existant et de devoir fractionner les pages.

Martin Smith
la source
9

Il va certainement recréer l'index non clusterisé et pas seulement mettre à jour les métadonnées. Ceci est testé sur SQL 2014 et ne devrait vraiment pas être testé sur un système de production:

CREATE TABLE [z](
    [a] [int] IDENTITY(1,1) NOT NULL,
    [b] [int] NOT NULL,
 CONSTRAINT [c_a] PRIMARY KEY CLUSTERED  ([a] ASC))
go
CREATE NONCLUSTERED INDEX [nc_b] on z (b asc)
GO
insert into z (b)
values (1);

Et maintenant pour la partie amusante:

DBCC IND (0, z, -1)

Cela nous donnera les pages de base de données où la table et l'index non cluster sont stockés.

Recherchez PagePIDIndexIDest 2 et PageTypeest 2, puis procédez comme suit:

DBCC TRACEON(3604) --are you sure that you are allowed to do this?

et alors:

dbcc page (0, 1, PagePID, 3) with tableresults

Notez qu'il y a un bitmap nul dans l'en-tête:

Extrait d'en-tête de page

Faisons maintenant:

alter table z alter Column b int null;

Si vous êtes vraiment impatient, vous pouvez réessayer d'exécuter la dbcc pagecommande mais elle échouera, alors vérifions à nouveau l'allocation avec DBCC IND (0, z, -1). La page se déplacera comme par magie.

Ainsi, la modification de la nullité d'une colonne affectera le stockage des index non cluster qui couvrent cette colonne, car les métadonnées doivent être mises à jour et vous ne devriez pas avoir besoin de reconstruire les index par la suite.


De nombreuses ALTER TABLE ... ALTER COLUMN ...opérations peuvent être effectuées à ONLINEpartir de SQL Server 2016, mais:

ALTER TABLE (Transact-SQL)

  • La modification d'une colonne de NOT NULLà NULLn'est pas prise en charge en tant qu'opération en ligne lorsque la colonne modifiée est référencée par des index non clusterisés.
Spörri
la source