Ubah kolom NULL dengan cepat menjadi TIDAK NULL

11

Saya memiliki tabel dengan jutaan baris dan kolom yang memungkinkan nilai NULL. Namun saat ini tidak ada baris yang memiliki nilai NULL untuk kolom itu (saya bisa memverifikasi ini dengan cukup cepat dengan kueri). Namun ketika saya menjalankan perintah

ALTER TABLE MyTable ALTER COLUMN MyColumn BIGINT NOT NULL;

pertanyaannya relatif selamanya . Ini sebenarnya membutuhkan waktu antara 10 dan 20 menit, lebih dari dua kali lebih lama dari menambahkan batasan pemeriksaan. Apakah ada cara untuk langsung memperbarui metadata tabel untuk kolom itu, terutama karena saya tahu bahwa tidak ada baris yang memiliki nilai NULL untuk kolom itu?

Joseph Daigle
sumber
2
Tidak (atau setidaknya tidak menggunakan metode yang terdokumentasi / didukung). Lihat Mengapa ALTER COLUMN untuk TIDAK NULL menyebabkan pertumbuhan file log yang besar?
Martin Smith
2
Mungkin juga sedang menunggu Sch-Mkunci ketika dibutuhkan "selamanya". Apakah Anda melihat apakah sedang menunggu atau sibuk?
Martin Smith
@ MartinSmith Saya mengklarifikasi apa yang saya maksud selamanya . Saya menguji ini di lingkungan dev tanpa sesi lain mengenai database. Itu akhirnya selesai. Tetapi jawaban yang Anda tautkan menjelaskan mengapa ini membutuhkan waktu lama. Jika Anda dapat menulis ulang komentar Anda sebagai jawaban, maka saya akan menerimanya.
Joseph Daigle

Jawaban:

12

@ ypercube menjawab bahwa sebagian ini dikelola hanya sebagai metadata yang berubah.

Menambahkan kendala dengan NOCHECKberarti bahwa tidak ada baris yang perlu dibaca untuk memverifikasinya, dan jika Anda mulai dari posisi di mana kolom tidak mengandung NULLnilai (dan jika Anda tahu tidak akan ditambahkan antara memeriksa dan menambahkan kendala), karena kendala mencegah NULLnilai yang dibuat dari masa depan INSERTatau UPDATEoperasi, ini akan berhasil.

Menambahkan kendala masih dapat berdampak pada transaksi bersamaan. Yang ALTER TABLEperlu mendapatkan Sch-Mkunci terlebih dahulu. Sementara menunggu, semua akses tabel lainnya akan diblokir seperti dijelaskan di sini .

Namun begitu Sch-Mkunci diperoleh, operasi harus cukup cepat.

Satu masalah dengan ini adalah bahwa bahkan jika Anda tahu kolom sebenarnya tidak memiliki NULLkendala tidak dipercaya oleh pengoptimal permintaan yang berarti bahwa rencana dapat menjadi kurang optimal.

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)

Rencana

Bandingkan ini dengan yang lebih sederhana

ALTER TABLE T ALTER COLUMN X INT NOT NULL

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

Rencana

Salah satu masalah yang mungkin Anda temui dengan mengubah definisi kolom dengan cara ini adalah bahwa itu tidak hanya perlu membaca semua baris untuk memverifikasi bahwa mereka memenuhi persyaratan tetapi juga dapat benar-benar melakukan pembaruan log masuk ke baris .

Rumah setengah jalan yang mungkin adalah menambahkan kendala pemeriksaan WITH CHECK. Ini akan lebih lambat daripada WITH NOCHECKkarena harus membaca semua baris tetapi memungkinkan pengoptimal kueri untuk memberikan rencana yang lebih sederhana dalam kueri di atas dan harus menghindari kemungkinan pembaruan yang dicatat.

Martin Smith
sumber
7

Anda bisa, alih-alih mengubah kolom, menambahkan CHECKbatasan tabel dengan NOCHECKopsi:

ALTER TABLE MyTable WITH NOCHECK
  ADD  CONSTRAINT MyColumn_NOT_NULL 
    CHECK (MyColumn IS NOT NULL) ;
ypercubeᵀᴹ
sumber
1
Ini akan mencegah pembaruan di masa mendatang atau sisipan yang membuat kolom NULLtetapi tidak akan dapat digunakan oleh pengoptimal kueri.
Martin Smith
@ MartinSmith Oh ya, saya baru saja membaca jawaban dan komentar di pertanyaan serupa: Bagaimana Anda menambahkan kolom NOT NULL ke tabel besar di SQL Server? Tolong, tambahkan jawaban dengan masalah atau solusi yang lebih baik dan saya akan menghapus milik saya.
ypercubeᵀᴹ
2
Saya tidak punya solusi yang lebih baik. Saya membatalkan ini karena menyediakan solusi parsial. Jika semua OP ingin lakukan adalah mencegah data yang tidak valid itu akan berfungsi (dan harus lebih cepat daripada ALTER COLUMNsetelah Sch-Mkunci diperoleh, ini tidak perlu memindai baris sama sekali). Hanya menunjukkan bahwa itu tidak persis sama (misalnya jika digunakan dalam NOT INkueri rencananya akan lebih kompleks)
Martin Smith