Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

Perubahan Kolom Metadata Saja Baru di SQL Server 2016

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:

  1. Ubah metadata di tabel sistem saja.
  2. Periksa semua data yang ada untuk kompatibilitas, lalu ubah metadata.
  3. 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 ke NULL untuk tipe data yang sama.
  • Meningkatkan ukuran maksimum varchar , nvarchar , atau varbinary kolom (kecuali max ).

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 atau PAGE kompresi.
    • Indeks dan partisi dapat menggunakan campuran dari tingkat kompresi ini. Yang penting tidak ada indeks atau partisi yang tidak terkompresi.
  • Berubah dari NULL ke NOT NULL tidak diizinkan .
  • Berikut perubahan tipe bilangan bulat didukung:
    • smallint ke integer atau bigint .
    • integer ke bigint .
    • smallmoney ke money (menggunakan representasi bilangan bulat secara internal).
  • Berikut ini perubahan tipe string dan biner didukung:
    • char(n) ke char(m) atau varchar(m)
    • nchar(n) ke nchar(m) atau nvarchar(m)
    • binary(n) ke binary(m) atau varbinary(m)
    • Semua hal di atas hanya untuk n < m dan m != 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 1
Kesalahan 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.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Konversi 'waktu' menjadi 'datetimeoffset' di SQL Server (Contoh T-SQL)

  2. PHP + SQL Server - Bagaimana cara mengatur charset untuk koneksi?

  3. SQL Server IF vs IIF():Apa Bedanya?

  4. Cara membuat RAND() Deterministik di SQL Server

  5. Mengapa CTE lebih baik daripada kursor/tabel turunan/subqueries/tabel temp dll?