Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Meminimalkan dampak pelebaran kolom IDENTITAS – bagian 2

[ Bagian 1 | Bagian 2 | Bagian 3 | Bagian 4 ]

Di bagian pertama seri ini, saya menunjukkan apa yang terjadi pada halaman fisik saat mengubah kolom IDENTITY dari int menjadi bigint. Untuk menjaga hal-hal sederhana, saya membuat tumpukan yang sangat sederhana tanpa indeks atau kendala. Sayangnya, sebagian besar dari kita tidak memiliki kemewahan semacam itu – meja penting yang perlu diubah tetapi tidak dapat dibuat ulang begitu saja dari awal, kemungkinan memiliki banyak atribut yang menghalangi kita. Dalam posting ini, saya ingin menunjukkan yang lebih umum, bahkan tanpa membahas hal-hal eksotis seperti In-Memory OLTP dan Columnstore.

Kunci Utama

Semoga semua tabel Anda memiliki kunci utama; jika kolom IDENTITY terlibat, bagaimanapun, tidak akan mudah untuk mengubah tipe data yang mendasarinya. Ambil contoh sederhana berikut, baik kunci utama berkerumun maupun tidak berkerumun:

CREATE TABLE dbo.Test1
(
  ID INT IDENTITY(1,1),
  CONSTRAINT PK_1 PRIMARY KEY NONCLUSTERED (ID)
);
 
CREATE TABLE dbo.Test2
(
  ID INT IDENTITY(1,1),
  CONSTRAINT PK_2 PRIMARY KEY CLUSTERED (ID)
);

Jika saya mencoba mengubah kolom:

ALTER TABLE dbo.Test1 ALTER COLUMN ID BIGINT;
GO
ALTER TABLE dbo.Test2 ALTER COLUMN ID BIGINT;

Saya mendapatkan sepasang pesan kesalahan untuk setiap ALTER (hanya menampilkan pasangan pertama):

Msg 5074, Level 16, Status 1
Objek 'PK_1' bergantung pada kolom 'ID'.
Msg 4922, Level 16, Status 9
ALTER TABLE ALTER COLUMN ID gagal karena satu atau lebih banyak objek mengakses kolom ini.

Ringkasan:Kita harus melepaskan kunci utama , apakah dikelompokkan atau tidak.

Indeks

Pertama mari kita ambil beberapa tabel seperti di atas, dan menggunakan indeks unik sebagai ganti kunci utama:

CREATE TABLE dbo.Test3
(
  ID INT IDENTITY(1,1),
  INDEX IX_3 UNIQUE NONCLUSTERED (ID)
);
 
CREATE TABLE dbo.Test4
(
  ID INT IDENTITY(1,1),
  INDEX IX_4 UNIQUE CLUSTERED (ID) 
);

Menjalankan perintah ALTER serupa di atas, mengarah ke pesan kesalahan yang sama. Ini tetap benar bahkan jika saya menonaktifkan indeks:

ALTER INDEX IX_3 ON dbo.Test3 DISABLE;
GO
ALTER INDEX IX_4 ON dbo.Test4 DISABLE;

Hasil serupa untuk berbagai jenis kombinasi indeks lainnya, seperti kolom yang disertakan atau filter:

CREATE TABLE dbo.Test5
(
  ID INT IDENTITY(1,1),
  x CHAR(1)
);
CREATE INDEX IX_5 ON dbo.Test5(x) INCLUDE(ID);
 
CREATE TABLE dbo.Test6
(
  ID INT IDENTITY(1,1),
  x CHAR(1)
);
CREATE INDEX IX_6 ON dbo.Test6(x) WHERE ID > 0;

Ringkasan:Kita perlu menghapus dan membuat ulang indeks apa pun , berkerumun atau tidak, yang mereferensikan kolom IDENTITAS – dalam kunci atau INCLUDE. Jika kolom IDENTITY adalah bagian dari indeks berkerumun, ini berarti semua indeks , karena mereka semua akan mereferensikan kunci pengelompokan menurut definisi. Dan menonaktifkannya saja tidak cukup.

Kolom yang Dihitung

Meskipun ini seharusnya relatif jarang, saya telah melihat kolom yang dihitung berdasarkan kolom IDENTITY. Misalnya:

CREATE TABLE dbo.Test7
(
  ID INT IDENTITY(1,1),
  NextID AS (ID + 1)
);

Kali ini, ketika kami mencoba mengubah, kami mendapatkan pasangan kesalahan yang sama, tetapi dengan teks yang sedikit berbeda:

Msg 5074, Level 16, Status 1
Kolom 'NextID' bergantung pada kolom 'ID'.
Msg 4922, Level 16, State 9
ALTER TABLE ALTER COLUMN ID gagal karena satu atau lebih banyak objek mengakses kolom ini.

Ini bahkan benar jika kita mengubah definisi kolom yang dihitung agar sesuai dengan tipe data target:

CREATE TABLE dbo.Test8
(
  ID INT IDENTITY(1,1),
  NextID AS (CONVERT(BIGINT, ID) + 1)
);

Ringkasan:Kita perlu mengubah definisi kolom yang dihitung, atau menghapusnya sama sekali.

Tampilan Terindeks

Tampilan yang diindeks juga melihat bagian penggunaannya yang adil. Mari kita buat tampilan terindeks yang bahkan tidak mereferensikan kolom IDENTITY (perhatikan tidak ada indeks atau batasan lain pada tabel dasar):

CREATE TABLE dbo.Test9
(
  ID INT IDENTITY(1,1),
  x CHAR(1)
);
GO
 
CREATE VIEW dbo.vTest9A
WITH SCHEMABINDING
AS
  SELECT x, c = COUNT_BIG(*)
    FROM dbo.Test9
    GROUP BY x;
GO
 
CREATE UNIQUE CLUSTERED INDEX IX_9A ON dbo.vTest9A(x);

Sekali lagi, kami akan mencoba ALTER, dan kali ini berhasil . Saya akui saya terkejut dengan ini, karena SCHEMABINDING seharusnya mencegah perubahan apa pun pada tabel yang mendasarinya, tetapi dalam hal ini hanya berlaku untuk kolom yang secara eksplisit direferensikan dalam tampilan. Jika kita membuat tampilan yang sedikit berbeda:

CREATE VIEW dbo.vTest9B
WITH SCHEMABINDING
AS
  SELECT ID, c = COUNT_BIG(*)
    FROM dbo.Test9
    GROUP BY ID;
GO
CREATE UNIQUE CLUSTERED INDEX IX_9B ON dbo.vTest9B(ID);

Sekarang kita akan gagal karena ketergantungan kolom:

Msg 5074, Level 16, Status 1
Objek 'vTest9B' bergantung pada kolom 'ID'.
Msg 4922, Level 16, Status 9
ALTER TABLE ALTER COLUMN ID gagal karena satu atau lebih banyak objek mengakses kolom ini.

Ringkasan:Kami harus menghapus semua indeks pada tampilan apa pun yang secara eksplisit merujuk kolom IDENTITY , serta semua indeks pada tampilan apa pun yang mereferensikan kolom IDENTITY dalam indeks berkerumunnya.

Kunci Asing Masuk

Mungkin aspek yang paling bermasalah dari kunci utama IDENTITY adalah bahwa dengan sifat pengganti, intinya sering menggunakan kunci pengganti ini di beberapa tabel terkait. Sekarang, saya tidak akan menganjurkan untuk menghindari integritas referensial, tetapi ini berpotensi akan sedikit menghalangi kita di sini juga. Kita tahu dari atas bahwa kita tidak dapat mengubah kolom yang merupakan bagian dari kunci utama atau batasan unik, dan agar tabel lain menunjuk ke sini dengan batasan kunci asing, salah satu dari dua hal itu harus ada. Jadi katakanlah kita memiliki dua tabel berikut:

CREATE TABLE dbo.TestParent
(
  ID INT IDENTITY(1,1),
  CONSTRAINT PK_Parent PRIMARY KEY CLUSTERED(ID)
);
GO
 
CREATE TABLE dbo.TestChild
(
  ParentID INT NOT NULL,
  CONSTRAINT FK_Parent FOREIGN KEY(ParentID) REFERENCES dbo.TestParent(ID)
);

Sebelum kita dapat mempertimbangkan untuk mengubah tipe data kolom, kita perlu menghilangkan batasan:

ALTER TABLE dbo.TestParent DROP CONSTRAINT PK_Parent;

Dan tentu saja kita tidak bisa, tanpa juga menghilangkan batasan kunci asing, karena ini menghasilkan pesan kesalahan berikut:

Msg 3725, Level 16, State 0
Konstrain 'PK_Parent' dirujuk oleh tabel 'TestChild', batasan foreign key 'FK_Parent'.
Msg 3727, Level 16, State 0
Bisa tidak menjatuhkan kendala. Lihat kesalahan sebelumnya.

Kesalahan ini tetap ada bahkan jika kita menonaktifkan batasan kunci asing terlebih dahulu:

ALTER TABLE dbo.TestChild NOCHECK CONSTRAINT FK_Parent;

Selain itu, pertimbangkan bahwa Anda juga memerlukan kolom referensi untuk mengubah tipe datanya. Dan selanjutnya, kolom tersebut kemungkinan berpartisipasi dalam beberapa elemen di atas yang mungkin juga mencegah perubahan pada tabel anak. Untuk mendapatkan semuanya secara copacetic dan sinkron, kita harus:

  • lepaskan batasan dan indeks yang relevan pada tabel induk
  • lepaskan batasan kunci asing yang relevan pada tabel anak
  • lepaskan indeks apa pun pada tabel anak yang mereferensikan kolom FK (dan menangani kolom terkomputasi yang relevan/tampilan yang diindeks)
  • mengubah tipe data pada tabel induk dan semua tabel anak
  • buat ulang semuanya

Ringkasan:Kita perlu melepaskan kunci asing yang masuk dan, berpotensi, ini akan memiliki banyak efek berjenjang. Menonaktifkan kunci asing saja tidak cukup, dan bagaimanapun juga tidak akan menjadi solusi permanen, karena tipe data pada akhirnya harus berubah di tabel anak juga.

Kesimpulan

Saya tahu sepertinya kita bergerak perlahan, dan saya akui bahwa dalam posting ini saya tampaknya menarik diri dari solusi daripada menuju satu. Saya akan sampai di sana, hanya ada banyak informasi untuk disajikan terlebih dahulu, termasuk hal-hal yang membuat perubahan jenis ini sulit. Diambil dari ringkasan di atas, kita perlu:

  • lepas dan buat kembali indeks yang relevan di tabel utama
  • ubah atau jatuhkan kolom terhitung yang melibatkan kolom IDENTITY
  • lepaskan indeks pada tampilan terindeks yang mereferensikan kolom IDENTITY
  • berurusan dengan kunci asing masuk yang mengarah ke kolom IDENTITAS

Sayangnya, banyak dari hal-hal ini adalah tangkapan-22. Anda tidak dapat mengubah kolom karena indeks bergantung padanya, dan Anda tidak dapat mengubah indeks hingga kolom berubah. Bukankah lebih bagus jika ALTER INDEX mendukung REBUILD WITH (ONLINE = ON, CHANGE_COLUMN (COLUMN = ID, NEW_TYPE = BIGINT)) ? Dan CASCADE_CHANGE_TO_REFERENCING_KEYS,COLUMNS,INDEXES,VIEWS,ETC ? Yah, tidak (saya memeriksa). Jadi, kita perlu menemukan cara untuk membuat hal-hal ini lebih mudah. Nantikan Bagian 3.

[ Bagian 1 | Bagian 2 | Bagian 3 | Bagian 4 ]


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Persamaan dan Perbedaan Fungsi RANK, DENSE_RANK dan ROW_NUMBER

  2. Menangani Kebocoran Sumber Daya GDI

  3. Gabung Dalam SQL

  4. Pengoptimalan Basis Data:Indeks

  5. Fragmentasi Indeks Clustered yang Tak Terduga