ALTER TABLE ... ALTER COLUMN
perintah sangat kuat. Anda dapat menggunakannya untuk mengubah tipe data kolom, panjang, presisi, skala, nullability, susunan...dan banyak hal lain selain itu.
Ini tentu lebih nyaman daripada alternatifnya:Membuat tabel baru dan memigrasikan data setiap kali diperlukan perubahan. Namun demikian, hanya ada begitu banyak yang dapat dilakukan untuk menyembunyikan kompleksitas yang mendasarinya. Seiring dengan sejumlah besar pembatasan pada apa yang bahkan mungkin dengan perintah ini, selalu ada pertanyaan tentang kinerja.
Pada akhirnya, tabel disimpan sebagai urutan byte dengan beberapa metadata di tempat lain dalam sistem untuk menjelaskan apa arti masing-masing byte tersebut, dan bagaimana mereka berhubungan dengan masing-masing berbagai kolom tabel. Ketika kami meminta SQL Server untuk mengubah beberapa aspek definisi kolom, itu perlu memeriksa apakah data yang ada kompatibel dengan definisi baru. Itu juga perlu menentukan apakah tata letak fisik saat ini perlu diubah.
Tergantung pada jenis perubahan dan konfigurasi database, sebuah ALTER COLUMN
perintah perlu melakukan salah satu tindakan berikut:
- Ubah metadata di tabel sistem saja.
- Periksa semua data yang ada untuk kompatibilitas, lalu ubah metadata.
- Tulis ulang beberapa atau semua data yang disimpan agar sesuai dengan definisi baru.
Opsi 1 mewakili kasus ideal dari sudut pandang kinerja. Ini hanya membutuhkan sedikit perubahan pada tabel sistem, dan sedikit pencatatan. Operasi masih memerlukan modifikasi skema terbatas Sch-M
lock, tetapi perubahan metadata itu sendiri akan selesai dengan sangat cepat, berapa pun ukuran tabelnya.
Perubahan Hanya-Metadata
Ada beberapa kasus khusus yang harus diwaspadai, tetapi sebagai ringkasan umum, tindakan berikut hanya memerlukan perubahan pada metadata:
- Berangkat dari
NOT NULL
keNULL
untuk tipe data yang sama. - Meningkatkan ukuran maksimum
varchar
,nvarchar
, atauvarbinary
kolom (kecualimax
).
Peningkatan di SQL Server 2016
Subjek posting ini adalah perubahan tambahan yang diaktifkan untuk metadata saja dari SQL Server 2016 dan seterusnya . Tidak ada perubahan sintaks yang diperlukan, dan tidak ada pengaturan konfigurasi yang perlu diubah. Anda mendapatkan peningkatan yang tidak terdokumentasi ini secara gratis.
Kemampuan baru menargetkan subset dari panjang tetap tipe data. Kemampuan baru berlaku untuk tabel penyimpanan baris dalam keadaan berikut:
- Kompresi harus diaktifkan:
- Di semua indeks dan partisi , termasuk tumpukan dasar atau indeks berkerumun.
- Salah satu
ROW
atauPAGE
kompresi. - Indeks dan partisi dapat menggunakan campuran dari tingkat kompresi ini. Yang penting tidak ada indeks atau partisi yang tidak terkompresi.
- Berubah dari
NULL
keNOT NULL
tidak diizinkan . - Berikut perubahan tipe bilangan bulat didukung:
smallint
keinteger
ataubigint
.integer
kebigint
.smallmoney
kemoney
(menggunakan representasi bilangan bulat secara internal).
- Berikut ini perubahan tipe string dan biner didukung:
char(n)
kechar(m)
atauvarchar(m)
nchar(n)
kenchar(m)
ataunvarchar(m)
binary(n)
kebinary(m)
atauvarbinary(m)
- Semua hal di atas hanya untuk
n < m
danm != max
- Perubahan susunan tidak diizinkan
Perubahan ini dapat berupa metadata saja karena tata letak data biner yang mendasarinya tidak berubah saat Descriptor Kolom format baris digunakan (karenanya perlu kompresi). Tanpa kompresi, penyimpanan baris menggunakan FixedVar yang asli representasi, yang tidak dapat mengakomodasi perubahan tipe data dengan panjang tetap ini tanpa menulis ulang tata letak fisik.
Anda mungkin memperhatikan bahwa tinyint
dihilangkan dari daftar tipe integer. Ini karena tidak ditandatangani, sedangkan tipe integer lainnya semua ditandatangani, jadi perubahan metadata saja tidak mungkin. Misalnya, nilai 255 dapat ditampung dalam satu byte untuk tinyint
, tetapi membutuhkan dua byte dalam salah satu format yang ditandatangani. Format yang ditandatangani dapat menampung -128 hingga +127 dalam satu byte saat dikompresi.
Contoh Bilangan Bulat
Salah satu aplikasi yang sangat berguna dari peningkatan ini adalah mengubah tipe data kolom dengan IDENTITY
properti.
Katakanlah kita memiliki tabel heap berikut menggunakan kompresi baris (kompresi halaman juga akan berfungsi):
DROP TABLE IF EXISTS dbo.Test; GO CREATE TABLE dbo.Test ( id integer IDENTITY NOT NULL, some_value integer NOT NULL ) WITH (DATA_COMPRESSION = ROW);
Mari tambahkan 5 juta baris data. Ini akan cukup untuk memperjelas (dari sudut pandang kinerja) apakah mengubah tipe data kolom adalah operasi metadata saja atau tidak:
WITH Numbers AS ( SELECT n = ROW_NUMBER() OVER (ORDER BY @@SPID) FROM sys.all_columns AS AC1 CROSS JOIN sys.all_columns AS AC2 ORDER BY n OFFSET 0 ROWS FETCH FIRST 5 * 1000 * 1000 ROWS ONLY ) INSERT dbo.Test WITH (TABLOCKX) ( some_value ) SELECT N.n FROM Numbers AS N;
Selanjutnya kita akan melakukan reseed pada IDENTITY
untuk membuatnya tampak seperti kita hampir kehabisan nilai yang sesuai dengan integer
:
DBCC CHECKIDENT ( N'dbo.Test', RESEED, 2147483646 );
Kami berhasil menambahkan satu baris lagi:
INSERT dbo.Test (some_value) VALUES (123456);
Tetapi mencoba menambahkan baris lain:
INSERT dbo.Test (some_value) VALUES (7890);
Menghasilkan pesan kesalahan:
Msg 8115, Level 16, Status 1, Baris 1Kesalahan overflow aritmatika mengubah IDENTITY menjadi tipe data int.
Kita dapat memperbaikinya dengan mengubah kolom menjadi bigint
:
ALTER TABLE dbo.Test ALTER COLUMN id bigint NOT NULL;
Berkat peningkatan di SQL Server 2016, perintah ini mengubah hanya metadata , dan segera selesai. INSERT
sebelumnya pernyataan (salah satu yang melemparkan kesalahan aritmatika overflow) sekarang berhasil diselesaikan.
Kemampuan baru ini tidak menyelesaikan semua masalah seputar mengubah jenis kolom dengan IDENTITY
Properti. Kami masih perlu menghapus dan membuat ulang indeks apa pun di kolom, membuat ulang kunci asing referensi, dan seterusnya. Itu sedikit di luar cakupan posting ini (meskipun Aaron Bertrand telah menulis tentang itu sebelumnya). Mampu mengubah tipe sebagai operasi metadata saja tentu tidak ada salahnya. Dengan perencanaan yang matang, langkah-langkah lain yang diperlukan dapat dibuat seefisien mungkin, misalnya menggunakan login minimal atau ONLINE
operasi.
Hati-hati dengan Sintaks
Pastikan untuk selalu tentukan NULL
atau NOT NULL
saat mengubah tipe data dengan ALTER COLUMN
. Katakanlah misalnya kita ingin juga mengubah tipe data dari some_value
kolom di tabel pengujian kami dari integer NOT NULL
ke bigint NOT NULL
.
Saat kita menulis perintah, kita menghilangkan NULL
atau NOT NULL
kualifikasi:
ALTER TABLE dbo.Test ALTER COLUMN some_value bigint;
Perintah ini berhasil diselesaikan sebagai perubahan metadata saja, tetapi juga menghapus NOT NULL
paksaan. Kolom sekarang bigint NULL
, yang tidak kami maksudkan. Perilaku ini didokumentasikan, tetapi mudah untuk diabaikan.
Kami mungkin mencoba memperbaiki kesalahan kami dengan:
ALTER TABLE dbo.Test ALTER COLUMN some_value bigint NOT NULL;
Ini bukan perubahan metadata saja. Kami tidak diperbolehkan untuk mengubah dari NULL
ke NOT NULL
(lihat kembali tabel sebelumnya jika Anda membutuhkan penyegaran pada kondisi). SQL Server perlu memeriksa semua nilai yang ada untuk memastikan tidak ada nol. Kemudian secara fisik menulis ulang setiap baris dari meja. Selain lambat, tindakan ini menghasilkan banyak log transaksi, yang dapat berdampak langsung.
Sebagai catatan tambahan, kesalahan yang sama ini tidak mungkin dilakukan untuk kolom dengan IDENTITY
Properti. Jika kita menulis ALTER COLUMN
pernyataan tanpa NULL
atau NOT NULL
dalam hal ini, mesin akan membantu mengasumsikan yang kami maksud adalah NOT NULL
karena properti identitas tidak diperbolehkan pada kolom nullable. Masih merupakan ide bagus untuk tidak bergantung pada perilaku ini.
Selalu tentukan NULL
atau NOT NULL
dengan ALTER COLUMN
.
Koleksi
Perhatian khusus diperlukan saat mengubah kolom string yang memiliki susunan yang tidak cocok dengan default untuk database.
Misalnya, katakanlah kita memiliki tabel dengan susunan huruf besar-kecil dan aksen-sensitif (anggap default database berbeda):
DROP TABLE IF EXISTS dbo.Test2; GO CREATE TABLE dbo.Test2 ( id integer IDENTITY NOT NULL, some_string char(8) COLLATE Latin1_General_100_CS_AS NOT NULL ) WITH (DATA_COMPRESSION = ROW);
Tambahkan 5 juta baris data:
WITH Numbers AS ( SELECT n = ROW_NUMBER() OVER (ORDER BY @@SPID) FROM sys.all_columns AS AC1 CROSS JOIN sys.all_columns AS AC2 ORDER BY n OFFSET 0 ROWS FETCH FIRST 5 * 1000 * 1000 ROWS ONLY ) INSERT dbo.Test2 WITH (TABLOCKX) ( some_string ) SELECT CONVERT(char(8), N.n) COLLATE Latin1_General_100_CS_AS FROM Numbers AS N;
Gandakan panjang kolom string menggunakan perintah berikut:
ALTER TABLE dbo.Test2 ALTER COLUMN some_string char(16) NOT NULL;
Kami ingat untuk menentukan NOT NULL
, tetapi lupa tentang susunan non-default. SQL Server mengasumsikan bahwa kami bermaksud mengubah susunan ke default basis data (Latin1_General_CI_AS
untuk basis data pengujian saya). Mengubah susunan mencegah operasi menjadi metadata saja, sehingga operasi berjalan selama beberapa menit, menghasilkan tumpukan log.
Buat ulang tabel dan data menggunakan skrip sebelumnya, lalu coba ALTER COLUMN
perintah lagi, tetapi menentukan susunan non-default yang ada sebagai bagian dari perintah:
ALTER TABLE dbo.Test2 ALTER COLUMN some_string char(16) COLLATE Latin1_General_100_CS_AS NOT NULL;
Perubahan sekarang segera selesai, sebagai operasi metadata saja. Seperti halnya NULL
dan NOT NULL
sintaks, membayar untuk menjadi eksplisit untuk menghindari kecelakaan. Ini adalah saran yang bagus secara umum, bukan hanya untuk ALTER COLUMN
.
Kompresi
Perlu diketahui bahwa kompresi perlu ditentukan secara eksplisit untuk setiap indeks, dan secara terpisah untuk tabel dasar jika merupakan heap. Ini adalah contoh lain di mana menggunakan sintaks atau pintasan yang disingkat dapat mencegah hasil yang diinginkan.
Misalnya, tabel berikut tidak menentukan kompresi eksplisit untuk kunci utama atau definisi indeks sebaris:
CREATE TABLE dbo.Test ( id integer IDENTITY NOT NULL PRIMARY KEY, some_value integer NOT NULL INDEX [IX dbo.Test some_value] ) WITH (DATA_COMPRESSION = PAGE);
PRIMARY KEY
akan memiliki nama yang ditetapkan, default ke CLUSTERED
, dan jadilah PAGE
terkompresi. Indeks sebaris akan menjadi NONCLUSTERED
dan tidak terkompresi sama sekali. Tabel ini tidak akan diaktifkan untuk pengoptimalan baru karena tidak semua indeks dan partisi dikompresi.
Definisi tabel yang jauh lebih baik dan lebih eksplisit adalah:
CREATE TABLE dbo.Test ( id integer IDENTITY NOT NULL CONSTRAINT [PK dbo.Test id] PRIMARY KEY CLUSTERED WITH (DATA_COMPRESSION = PAGE), some_value integer NOT NULL INDEX [IX dbo.Test some_value] NONCLUSTERED WITH (DATA_COMPRESSION = ROW) );
Tabel ini akan memenuhi syarat untuk pengoptimalan baru karena semua indeks dan partisi dikompresi. Seperti disebutkan sebelumnya, pencampuran jenis kompresi tidak masalah.
Ada berbagai cara untuk menulis CREATE TABLE
ini pernyataan secara eksplisit, sehingga ada unsur preferensi pribadi. Poin penting yang dapat diambil adalah untuk selalu eksplisit tentang apa yang Anda inginkan. Ini berlaku untuk CREATE INDEX
yang terpisah pernyataan juga.
Acara yang Diperpanjang dan Bendera Jejak
Ada acara tambahan khusus untuk ALTER COLUMN
khusus metadata baru operasi yang didukung di SQL Server 2016 dan seterusnya.
Acara yang diperpanjang adalah compressed_alter_column_is_md_only
di Debug saluran. Bidang acaranya adalah object_id
, column_id
, dan is_md_only
(benar/salah).
Peristiwa ini hanya menunjukkan jika operasi hanya metadata karena kemampuan baru SQL Server 2016. Perubahan kolom yang hanya metadata sebelum 2016 akan menampilkan is_md_only = false
meskipun masih hanya metadata.
Acara tambahan lainnya yang berguna untuk melacak ALTER COLUMN
operasi termasuk metadata_ddl_alter_column
dan alter_column_event
, keduanya di Analitik saluran.
Jika Anda perlu menonaktifkan kemampuan SQL Server 2016 yang baru untuk alasan apa pun, bendera pelacakan global (atau start-up) yang tidak berdokumen 3618 dapat digunakan. Bendera pelacakan ini tidak efektif bila digunakan pada tingkat sesi. Tidak ada cara untuk menentukan tanda pelacakan tingkat kueri dengan ALTER COLUMN
perintah.
Pemikiran Terakhir
Mampu mengubah beberapa tipe data integer dengan panjang tetap dengan perubahan metadata saja merupakan peningkatan produk yang sangat disambut baik. Itu memang mengharuskan tabel sudah dikompresi sepenuhnya, tetapi itu menjadi lebih umum. Ini terutama benar karena kompresi diaktifkan di semua edisi yang dimulai dengan SQL Server 2016 Service Pack 1.
Kolom tipe string dengan panjang tetap mungkin kurang umum. Beberapa di antaranya mungkin karena pertimbangan yang agak ketinggalan zaman seperti penggunaan ruang. Saat dikompresi, kolom string dengan panjang tetap tidak menyimpan trailing blank, membuatnya sama efisiennya dengan kolom string dengan panjang variabel dari sudut pandang penyimpanan. Memang menjengkelkan untuk memangkas ruang untuk manipulasi atau tampilan, tetapi jika data biasanya menempati sebagian besar panjang maksimum, jenis panjang tetap dapat memiliki keuntungan penting, paling tidak mengenai pemberian memori untuk hal-hal seperti penyortiran dan hashing.
Tidak semua kabar baik dengan kompresi diaktifkan. Saya sebutkan sebelumnya bahwa SQL Server terkadang dapat melakukan perubahan metadata saja setelah memeriksa bahwa semua nilai yang ada akan berhasil dikonversi ke tipe baru. Ini adalah kasus ketika menggunakan ALTER COLUMN
untuk mengubah dari integer
ke smallint
Misalnya. Sayangnya, operasi ini saat ini tidak hanya metadata untuk objek terkompresi.
Ucapan Terima Kasih
Terima kasih khusus kepada Panagiotis Antonopoulos (Insinyur Perangkat Lunak Utama) dan Mirek Sztajno (Manajer Program Senior) dari tim produk SQL Server atas bantuan dan bimbingan mereka selama penelitian dan penulisan artikel ini.
Tak satu pun dari detail yang diberikan dalam karya ini harus dianggap sebagai dokumentasi resmi Microsoft atau pernyataan produk.