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).
Comme mentionné par @Souplex dans les commentaires, une explication possible pourrait être si cette colonne est la première colonne capable de NULL
dans 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é ix
a 248 pages feuilles et une seule page racine.
Une ligne typique dans une page feuille d'index ressemble à
Et dans la page racine
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 à
et les lignes dans les pages de niveau supérieur comme ci-dessous.
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
Cela crée une toute nouvelle copie de l'index plutôt que de mettre à jour l'existant et de devoir fractionner les pages.
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 PagePID
où IndexID
vaut 2 et PageType
vaut 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:
Faisons maintenant:
alter table z alter Column b int null;
Si vous êtes vraiment impatient, vous pouvez essayer d'exécuter à nouveau la commande dbcc page
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 opérations ALTER TABLE ... ALTER COLUMN ...
Peuvent être effectuées ONLINE
à partir de SQL Server 2016, mais:
- La modification d'une colonne de
NOT NULL
EnNULL
n'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.