Dalam upaya untuk memisahkan aplikasi dari database monolitik kami, kami telah mencoba untuk mengubah kolom INT IDENTITY dari berbagai tabel menjadi kolom dihitung PERSISTED yang menggunakan COALESCE. Pada dasarnya, kita memerlukan aplikasi yang dipisahkan kemampuan untuk masih memperbarui database untuk data umum yang dibagikan di banyak aplikasi sambil tetap memungkinkan aplikasi yang ada untuk membuat data dalam tabel ini tanpa perlu modifikasi kode atau prosedur.
Jadi pada dasarnya, kami telah pindah dari definisi kolom tentang;
PkId INT IDENTITY(1,1) PRIMARY KEY
untuk;
PkId AS AS COALESCE(old_id, external_id, new_id) PERSISTED NOT NULL,
old_id INT NULL, -- Values here are from existing records of PkId before table change
external_id INT NULL,
new_id INT IDENTITY(2000000,1) NOT NULL
Dalam semua kasus PkId juga merupakan KUNCI UTAMA dan dalam semua kasus kecuali satu, itu CLUSTERED. Semua tabel memiliki kunci asing dan indeks yang sama seperti sebelumnya. Intinya, format baru memungkinkan PkId disediakan oleh aplikasi yang dipisahkan (sebagai external_id), tetapi juga memungkinkan PkId menjadi nilai kolom IDENTITY sehingga memungkinkan kode yang ada yang bergantung pada kolom IDENTITY melalui penggunaan SCOPE_IDENTITY dan @@ IDENTITY untuk bekerja seperti dulu.
Masalah yang kami miliki adalah bahwa kami telah menemukan beberapa pertanyaan yang biasanya berjalan dalam waktu yang dapat diterima untuk sekarang benar-benar meledak. Paket kueri yang dihasilkan yang digunakan oleh kueri ini tidak seperti dulu.
Mengingat kolom baru adalah KUNCI UTAMA, tipe data yang sama seperti sebelumnya, dan TERUS, saya akan mengharapkan permintaan dan rencana kueri untuk berperilaku sama seperti sebelumnya. Haruskah KOMPUTED INT PkId pada dasarnya berperilaku dengan cara yang sama seperti definisi INT eksplisit dalam hal bagaimana SQL Server akan menghasilkan rencana eksekusi? Adakah kemungkinan masalah lain dengan pendekatan ini yang bisa Anda lihat?
Tujuan dari perubahan ini seharusnya memungkinkan kami untuk mengubah definisi tabel tanpa perlu memodifikasi prosedur dan kode yang ada. Mengingat masalah ini, saya merasa kita tidak bisa menggunakan pendekatan ini.
sumber
Jawaban:
PERTAMA
Anda mungkin tidak perlu semua tiga kolom:
old_id
,external_id
,new_id
. Thenew_id
kolom, menjadiIDENTITY
, akan memiliki nilai baru yang dihasilkan untuk setiap baris, bahkan ketika Anda masukkan ke dalamexternal_id
. Tetapi, antaraold_id
danexternal_id
, itu cukup banyak saling eksklusif: apakah sudah adaold_id
nilai atau kolom itu, dalam konsepsi saat ini, hanya akanNULL
jika menggunakanexternal_id
ataunew_id
. Karena Anda tidak akan menambahkan id "eksternal" baru ke baris yang sudah ada (yaitu yang memilikiold_id
nilai), dan tidak akan ada nilai baru yang masukold_id
, maka mungkin ada satu kolom yang digunakan untuk kedua tujuan.Jadi, singkirkan
external_id
kolom dan ganti namaold_id
menjadi sesuatu sepertiold_or_external_id
atau apa pun. Ini seharusnya tidak memerlukan perubahan nyata untuk apa pun, namun mengurangi beberapa komplikasi. Paling-paling Anda mungkin perlu memanggil kolomexternal_id
, bahkan jika itu berisi nilai-nilai "lama", jika kode aplikasi sudah ditulis untuk dimasukkanexternal_id
.Itu mengurangi struktur baru menjadi adil:
Sekarang Anda hanya menambahkan 8 byte per baris, bukan 12 byte (dengan asumsi Anda tidak menggunakan
SPARSE
opsi atau Kompresi Data). Dan Anda tidak perlu mengubah kode apa pun, T-SQL atau kode Aplikasi.KEDUA
Melanjutkan jalan penyederhanaan ini, mari kita lihat apa yang tersisa:
old_or_external_id
Kolom baik memiliki nilai-nilai yang sudah, atau akan diberikan nilai baru dari aplikasi, atau akan ditinggalkan sebagaiNULL
.new_id
akan selalu memiliki nilai baru yang dihasilkan, tetapi nilai yang hanya akan digunakan jikaold_or_external_id
kolomNULL
.Tidak pernah ada saat ketika Anda membutuhkan nilai di keduanya
old_or_external_id
dannew_id
. Ya, akan ada saat-saat ketika kedua kolom memiliki nilai karenanew_id
menjadiIDENTITY
, tetapinew_id
nilai - nilai itu diabaikan. Sekali lagi, kedua bidang ini saling eksklusif. Jadi bagaimana sekarang?Sekarang kita bisa melihat mengapa kita membutuhkannya
external_id
. Menimbang bahwa dimungkinkan untuk memasukkan ke dalamIDENTITY
kolom menggunakanSET IDENTITY_INSERT {table_name} ON;
, Anda bisa lolos tanpa membuat perubahan skema sama sekali, dan hanya memodifikasi kode aplikasi Anda untuk membungkusINSERT
pernyataan / operasiSET IDENTITY_INSERT {table_name} ON;
danSET IDENTITY_INSERT {table_name} OFF;
pernyataan. Anda kemudian perlu menentukan rentang awal untuk meresetIDENTITY
kolom ke (untuk nilai yang baru dihasilkan) karena harus jauh di atas nilai-nilai yang akan disisipkan kode Aplikasi karena memasukkan nilai yang lebih tinggi akan menyebabkan nilai yang dihasilkan secara otomatis berikutnya menjadi lebih besar dari nilai MAX saat ini. Tetapi Anda selalu dapat memasukkan nilai yang di bawah nilai IDENT_CURRENT .Menggabungkan
old_or_external_id
dannew_id
kolom juga tidak meningkatkan peluang berlari ke situasi nilai yang tumpang tindih antara nilai yang dibuat secara otomatis dan nilai yang dihasilkan aplikasi karena maksud memiliki kolom 2, atau bahkan 3, adalah untuk menggabungkannya menjadi nilai Kunci Utama, dan itu selalu merupakan nilai unik.Dalam pendekatan ini, Anda hanya perlu:
Tinggalkan tabel sebagai:
Ini menambahkan 0 byte ke setiap baris, bukannya 8, atau bahkan 12.
SET IDENTITY_INSERT {table_name} ON;
danSET IDENTITY_INSERT {table_name} OFF;
pernyataan.KEDUA, Bagian B
Variasi pada pendekatan yang dicatat secara langsung di atas adalah memiliki nilai-nilai penyisipan kode aplikasi yang dimulai dengan -1 dan turun dari sana. Ini meninggalkan
IDENTITY
nilai - nilai sebagai satu-satunya yang naik . Manfaatnya di sini adalah Anda tidak hanya tidak memperumit skema, Anda juga tidak perlu khawatir akan mengalami ID yang tumpang tindih (jika nilai yang dihasilkan aplikasi masuk ke rentang baru yang dibuat secara otomatis). Ini hanya opsi jika Anda belum menggunakan nilai ID negatif (dan tampaknya sangat jarang bagi orang untuk menggunakan nilai negatif pada kolom yang dibuat secara otomatis sehingga ini kemungkinan kemungkinan dalam sebagian besar situasi).Dalam pendekatan ini, Anda hanya perlu:
Tinggalkan tabel sebagai:
Ini menambahkan 0 byte ke setiap baris, bukannya 8, atau bahkan 12.
-1
.SET IDENTITY_INSERT {table_name} ON;
danSET IDENTITY_INSERT {table_name} OFF;
pernyataan.Di sini Anda masih perlu melakukan
IDENTITY_INSERT
, tetapi: Anda tidak menambahkan kolom baru, tidak perlu "memasang kembali"IDENTITY
kolom apa pun , dan tidak memiliki risiko tumpang tindih di masa depan.KEDUA, Bagian 3
Satu variasi terakhir dari pendekatan ini adalah dengan menukar
IDENTITY
kolom dan menggunakan Sekuens . Alasan untuk mengambil pendekatan ini adalah untuk dapat memiliki nilai-nilai penyisipan kode aplikasi yang: positif, di atas rentang yang dibuat secara otomatis (tidak di bawah), dan tidak perluSET IDENTITY_INSERT ON / OFF
.Dalam pendekatan ini, Anda hanya perlu:
Salin
IDENTITY
kolom ke kolom baru yang tidak memilikiIDENTITY
properti, tetapi memilikiDEFAULT
kendala menggunakan fungsi VALUE FOR NEXT FOR :Ini menambahkan 0 byte ke setiap baris, bukannya 8, atau bahkan 12.
SET IDENTITY_INSERT {table_name} ON;
danSET IDENTITY_INSERT {table_name} OFF;
pernyataan.NAMUN , karena persyaratan bahwa kode dengan salah satu
SCOPE_IDENTITY()
atau@@IDENTITY
masih berfungsi dengan benar, beralih ke Sequences saat ini tidak menjadi pilihan karena tampaknya tidak ada yang setara dengan fungsi-fungsi untuk Sequences :-(. Sedih!sumber
IDENTITY_INSERT
, tetapi belum mengujinya. Tidak yakin opsi # 1 akan menyelesaikan masalah Anda secara keseluruhan, itu hanya pengamatan untuk mengurangi kerumitan yang tidak perlu. Namun, jika Anda memiliki beberapa utas yang memasukkan ID "eksternal" baru, bagaimana Anda menjamin bahwa mereka unik?IDENTITY_INSERT ON
untuk tabel yang sama dalam dua sesi dan memasukkan keduanya tanpa masalah.