Saya bekerja dengan database lama yang diimpor dari MS Access. Ada sekitar dua puluh tabel dengan kunci primer non-clustered, unik yang dibuat selama MS Access> peningkatan SQL Server.
Banyak dari tabel ini juga memiliki indeks unik, non-cluster yang merupakan duplikat dari kunci utama.
Saya mencoba untuk membersihkan ini.
Tapi apa yang saya temukan adalah setelah saya membuat kembali kunci utama sebagai indeks berkerumun, dan kemudian mencoba untuk membangun kembali kunci asing, kunci asing referensi lama, indeks duplikat (yang unik).
Saya tahu ini karena tidak akan membiarkan saya menjatuhkan indeks duplikat.
Saya akan berpikir SQL Server akan selalu memilih kunci utama jika ada. Apakah SQL Server memiliki metode pemilihan antara indeks unik dan kunci utama?
Untuk menduplikasi masalah (pada SQL Server 2008 R2):
IF EXISTS (SELECT * FROM sys.tables WHERE name = 'Child') DROP TABLE Child
GO
IF EXISTS (SELECT * FROM sys.tables WHERE name = 'Parent') DROP TABLE Parent
GO
-- Create the parent table
CREATE TABLE Parent (ParentID INT NOT NULL IDENTITY(1,1))
-- Make the parent table a heap
ALTER TABLE Parent ADD CONSTRAINT PK_Parent PRIMARY KEY NONCLUSTERED (ParentID)
-- Create the duplicate index on the parent table
CREATE UNIQUE NONCLUSTERED INDEX IX_Parent ON Parent (ParentID)
-- Create the child table
CREATE TABLE Child (ChildID INT NOT NULL IDENTITY(1,1), ParentID INT NOT NULL )
-- Give the child table a normal PKey
ALTER TABLE Child ADD CONSTRAINT PK_Child PRIMARY KEY CLUSTERED (ChildID)
-- Create a foreign key relationship with the Parent table on ParentID
ALTER TABLE Child ADD CONSTRAINT FK_Child FOREIGN KEY (ParentID)
REFERENCES Parent (ParentID) ON DELETE CASCADE NOT FOR REPLICATION
-- Try to clean this up
-- Drop the foreign key constraint on the Child table
ALTER TABLE Child DROP CONSTRAINT FK_Child
-- Drop the primary key constraint on the Parent table
ALTER TABLE Parent DROP CONSTRAINT PK_Parent
-- Recreate the primary key on Parent as a clustered index
ALTER TABLE Parent ADD CONSTRAINT PK_Parent PRIMARY KEY CLUSTERED (ParentID)
-- Recreate the foreign key in Child pointing to parent ID
ALTER TABLE Child ADD CONSTRAINT FK_Child FOREIGN KEY (ParentID)
REFERENCES Parent (ParentID) ON DELETE CASCADE NOT FOR REPLICATION
-- Try to drop the duplicate index on Parent
DROP INDEX IX_Parent ON Parent
Pesan kesalahan:
Msg 3723, Level 16, Negara 6, Baris 36 INDEX DROP eksplisit tidak diizinkan pada indeks 'Parent.IX_Parent'. Ini sedang digunakan untuk penegakan kendala ASING KUNCI.
Jawaban:
Dokumentasi (kurangnya) menunjukkan bahwa perilaku ini merupakan detail implementasi, dan karenanya tidak terdefinisi dan dapat berubah sewaktu-waktu.
Ini sangat kontras dengan CREATE FULLTEXT INDEX , di mana Anda harus menentukan nama indeks yang akan dilampirkan - AFAIK, tidak ada
FOREIGN KEY
sintaks yang tidak berdokumen untuk melakukan yang setara (walaupun secara teoritis, mungkin ada di masa depan).Seperti disebutkan, masuk akal bahwa SQL Server memilih indeks fisik terkecil untuk mengaitkan kunci asing. Jika Anda mengubah skrip untuk membuat batasan unik
CLUSTERED
, skrip "berfungsi" pada 2008 R2. Namun perilaku itu masih belum jelas dan tidak bisa diandalkan.Seperti sebagian besar aplikasi lawas, Anda hanya perlu turun ke hal-hal sepele dan membersihkan.
sumber
Setidaknya dimungkinkan untuk mengarahkan SqlServer ke referensi kunci utama, ketika kunci asing sedang dibuat dan kendala kunci alternatif atau indeks unik memang ada pada tabel yang direferensikan.
Jika kunci primer perlu direferensikan, maka hanya nama tabel yang direferensikan harus ditentukan dalam definisi kunci asing dan daftar kolom yang direferensikan harus dihilangkan:
Lebih detail di bawah ini.
Pertimbangkan pengaturan berikut:
di mana tabel
TRef
bermaksud ke tabel referensiT
.Untuk membuat batasan referensial seseorang dapat menggunakan
ALTER TABLE
perintah dengan dua alternatif:perhatikan bahwa dalam kasus kedua tidak ada kolom dari tabel yang direferensikan ditentukan (
REFERENCES T
versusREFERENCES T (id)
).Karena belum ada indeks kunci
T
, eksekusi perintah ini akan menghasilkan kesalahan.Perintah pertama mengembalikan kesalahan berikut:
Perintah kedua, bagaimanapun, mengembalikan kesalahan yang berbeda:
melihat bahwa dalam harapan kasus pertama adalah kunci primer atau kandidat , sedangkan dalam kasus kedua harapan adalah kunci primer saja.
Mari kita periksa apakah SqlServer akan menggunakan sesuatu selain kunci utama dengan perintah kedua atau tidak.
Jika kami menambahkan beberapa indeks unik dan kunci unik pada
T
:perintah untuk
FK_TRef_T_1
penciptaan berhasil, tetapi perintah untukFK_TRef_T_2
penciptaan masih gagal dengan Msg 1773.Akhirnya, jika kita menambahkan kunci utama pada
T
:perintah untuk
FK_TRef_T_2
penciptaan berhasil.Mari kita periksa indeks apa dari tabel
T
yang dirujuk oleh kunci asing tabelTRef
:ini mengembalikan:
lihat yang
FK_TRef_T_2
sesuai denganPK_T
.Jadi, ya, dengan menggunakan
REFERENCES T
sintaks kunci asingTRef
dipetakan ke kunci primerT
.Saya tidak dapat menemukan perilaku seperti yang dijelaskan dalam dokumentasi SqlServer secara langsung, tetapi Msg 1773 khusus menunjukkan bahwa itu tidak disengaja. Kemungkinan implementasi tersebut memberikan kepatuhan dengan Standar SQL, di bawah ini adalah kutipan singkat dari bagian 11.8 dari ANSI / ISO 9075-2: 2003
Transact-SQL mendukung dan memperluas ANSI SQL. Namun tidak sesuai dengan SQL Standard. Ada dokumen bernama SQL Server Transact-SQL ISO / IEC 9075-2 Dokumen Pendukung Standar (MS-TSQLISO02 secara singkat, lihat di sini ) yang menggambarkan tingkat dukungan yang disediakan oleh Transact-SQL. Dokumen mencantumkan ekstensi dan variasi ke standar. Sebagai contoh, dokumen ini menyatakan bahwa
MATCH
klausa tidak didukung dalam definisi kendala referensial. Tetapi tidak ada variasi terdokumentasi yang relevan dengan potongan standar yang dikutip. Jadi, pendapat saya adalah bahwa perilaku yang diamati cukup terdokumentasi.Dan dengan menggunakan
REFERENCES T (<reference column list>)
sintaksis tampaknya SqlServer memilih indeks nonclustered yang cocok pertama di antara indeks tabel yang direferensikan (satu dengan yangindex_id
tampaknya paling sedikit , bukan yang dengan ukuran fisik terkecil seperti yang diasumsikan dalam komentar pertanyaan), atau indeks berkelompok jika cocok dan tidak ada indeks nonclustered yang cocok. Perilaku seperti itu tampaknya konsisten sejak SqlServer 2008 (versi 10.0). Ini hanya pengamatan saja, tidak ada jaminan dalam hal ini.sumber