Mengubah kolom dari NOT NULL ke NULL - Apa yang terjadi di bawah tenda?

25

Kami memiliki tabel dengan baris 2.3B di dalamnya. Kami ingin mengubah kolom dari NOT NULL ke NULL. Kolom terkandung dalam satu indeks (bukan indeks berkerumun atau indeks PK). Tipe data tidak berubah (ini adalah INT). Hanya nullability. Pernyataan tersebut adalah sebagai berikut:

Alter Table dbo.Workflow Alter Column LineId Int NULL

Operasi membutuhkan lebih dari 10 sebelum kita menghentikannya (kita bahkan belum membiarkannya berjalan sampai selesai karena ini adalah operasi pemblokiran dan terlalu lama). Kami mungkin akan menyalin tabel ke server dev tes berapa lama sebenarnya dibutuhkan. Tapi, saya ingin tahu apakah ada yang tahu apa yang dilakukan SQL Server di bawah tenda ketika mengkonversi dari NOT NULL ke NULL? Juga, akankah indeks yang terpengaruh perlu dibangun kembali? Paket kueri yang dihasilkan tidak menunjukkan apa yang terjadi.

Tabel tersebut dikelompokkan (bukan tumpukan).

Randy Minder
sumber
2
Saya pikir itu harus memperbarui bitmap nol pada semua datapage tingkat daun. Dan dengan baris 2.3B, saya yakin itu akan memiliki banyak halaman untuk dikerjakan. Saya tidak terlalu yakin tentang ini.
Souplex
3
Bisa jadi sibuk menempatkan bitmap null pada indeks juga. Bitmap NULL TIDAK akan hadir dalam INDEKS NON-CLUSTERED jika semua bagian kolom dari definisi indeks didefinisikan sebagai NOT NULL.
Souplex

Jawaban:

27

Seperti disinggung oleh @Souplex di komentar satu penjelasan yang mungkin terjadi jika kolom ini adalah kolom pertama NULLdalam indeks non clustered yang diikutinya.

Untuk pengaturan berikut

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 menunjukkan indeks non-cluster ixmemiliki 248 halaman daun dan satu halaman root.

Seperti baris biasa di halaman daun indeks

masukkan deskripsi gambar di sini

Dan di halaman root

masukkan deskripsi gambar di sini

Lalu berlari ...

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

Kembali

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

Memeriksa kembali daun indeks, baris sekarang terlihat seperti

masukkan deskripsi gambar di sini

dan baris di halaman tingkat atas seperti di bawah ini.

masukkan deskripsi gambar di sini

Setiap baris telah diperbarui dan sekarang berisi dua byte untuk jumlah kolom bersama dengan byte lain untuk NULL_BITMAP.

Karena lebar baris tambahan indeks non-cluster sekarang memiliki 285 halaman daun dan sekarang dua halaman tingkat menengah bersama dengan halaman root.

Rencana eksekusi untuk

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

terlihat sebagai berikut

masukkan deskripsi gambar di sini

Ini menciptakan salinan baru indeks daripada memperbarui yang sudah ada dan perlu untuk membagi halaman.

Martin Smith
sumber
9

Ini pasti akan membuat kembali indeks yang tidak berkerumun dan tidak hanya memperbarui metadata. Ini diuji pada SQL 2014 dan harus benar-benar tidak diuji pada sistem produksi:

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

Dan sekarang untuk bagian yang menyenangkan:

DBCC IND (0, z, -1)

Ini akan memberi kita halaman basis data tempat tabel dan indeks yang tidak dikelompokkan disimpan.

Temukan PagePIDdimana IndexID2 dan PageType2, lalu lakukan hal berikut:

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

lalu:

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

Perhatikan bahwa ada bitmap nol di header:

Ekstrak tajuk halaman

Sekarang mari kita lakukan:

alter table z alter Column b int null;

Jika Anda benar-benar tidak sabar, Anda dapat mencoba menjalankan dbcc pageperintah lagi tetapi itu akan gagal, jadi mari kita periksa alokasi lagi dengan DBCC IND (0, z, -1). Halaman itu akan bergerak seolah-olah dengan sihir.

Jadi mengubah nullability kolom akan memengaruhi penyimpanan indeks non-cluster yang mencakup kolom itu, karena metadata perlu diperbarui dan Anda tidak perlu membangun kembali indeks setelahnya.


Banyak ALTER TABLE ... ALTER COLUMN ...operasi dapat dilakukan ONLINEdimulai dengan SQL Server 2016, tetapi:

ALTER TABLE (Transact-SQL)

  • Mengubah kolom dari NOT NULLmenjadi NULLtidak didukung sebagai operasi online ketika kolom yang diubah direferensikan oleh indeks yang tidak dikelompokkan.
Spörri
sumber