Modifica di una colonna da NOT NULL a NULL - Cosa succede sotto il cofano?


25

Abbiamo una tabella con 2.3B righe al suo interno. Vorremmo cambiare una colonna da NOT NULL a NULL. La colonna è contenuta in un indice (non nell'indice cluster o PK). Il tipo di dati non sta cambiando (è un INT). Solo la nullità. La dichiarazione è la seguente:

Alter Table dbo.Workflow Alter Column LineId Int NULL

L'operazione richiede oltre 10 prima di interromperla (non l'abbiamo ancora eseguita fino al completamento perché è un'operazione di blocco e stava impiegando troppo tempo). Probabilmente copieremo la tabella su un server di sviluppo per verificare quanto tempo impiega effettivamente. Ma sono curioso di sapere se qualcuno sa cosa sta facendo SQL Server sotto il cofano durante la conversione da NOT NULL a NULL? Inoltre, gli indici interessati dovranno essere ricostruiti? Il piano di query generato non indica cosa sta succedendo.

La tabella in questione è raggruppata (non un heap).


2
Penso che dovrebbe aggiornare la bitmap null su tutte le pagine dati a livello foglia. E con le righe 2.3B, scommetto che ci sarebbero molte pagine su cui lavorare. Non sono troppo sicuro di questo però.
souplex,

3
Potrebbe essere impegnato a posizionare una bitmap nulla sull'indice. La bitmap NULL NON sarà presente in un INDICE NON CLUSTER se tutte le parti della colonna della definizione dell'indice sono definite come NOT NULL.
souplex,

Risposte:


27

Come accennato da @Souplex nei commenti, una possibile spiegazione potrebbe essere se questa colonna è la prima NULLcolonna dell'indice a cui non partecipa.

Per la seguente configurazione

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 mostra che l'indice non cluster ixha 248 pagine foglia e una singola pagina radice.

Assomiglia a una riga tipica in una pagina foglia indice

inserisci qui la descrizione dell'immagine

E nella pagina principale

inserisci qui la descrizione dell'immagine

Quindi in esecuzione ...

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

tornati

+-----------------+--------------------+-------------+----------------+
|    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 |
+-----------------+--------------------+-------------+----------------+

Ricontrollando nuovamente la foglia indice, ora sembrano le righe

inserisci qui la descrizione dell'immagine

e le righe nelle pagine di livello superiore come di seguito.

inserisci qui la descrizione dell'immagine

Ogni riga è stata aggiornata e ora contiene due byte per il conteggio delle colonne insieme a un altro byte per NULL_BITMAP.

A causa della larghezza della riga aggiuntiva, l'indice non cluster ora ha 285 pagine foglia e ora due pagine di livello intermedio insieme alla pagina principale.

Il piano di esecuzione per il

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

sembra come segue

inserisci qui la descrizione dell'immagine

Ciò crea una nuova copia dell'indice anziché aggiornare quella esistente e deve dividere le pagine.


9

Creerà sicuramente l'indice non cluster e non solo aggiornerà i metadati. Questo è testato su SQL 2014 e non dovrebbe davvero essere testato su un sistema di produzione:

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);

E ora per la parte divertente:

DBCC IND (0, z, -1)

Questo ci fornirà le pagine del database in cui sono memorizzati la tabella e l'indice non cluster.

Trova PagePIDdove IndexIDè 2 ed PageTypeè 2, quindi procedi come segue:

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

e poi:

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

Si noti che nell'intestazione è presente una bitmap nulla:

Estratto dell'intestazione della pagina

Adesso facciamo:

alter table z alter Column b int null;

Se sei davvero impaziente, puoi provare a eseguire dbcc pagenuovamente il comando ma fallirà, quindi controlliamo di nuovo l'allocazione con DBCC IND (0, z, -1). La pagina si sarà spostata come per magia.

Pertanto, la modifica della nullabilità di una colonna influirà sulla memorizzazione di indici non cluster che coprono quella colonna, poiché i metadati devono essere aggiornati e non è necessario ricostruire gli indici in seguito.


Molte ALTER TABLE ... ALTER COLUMN ...operazioni possono essere eseguite a ONLINEpartire da SQL Server 2016, ma:

ALTER TABLE (Transact-SQL)

  • La modifica di una colonna da NOT NULLa NULLnon è supportata come operazione online quando la colonna modificata viene referenziata da indici non cluster.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.