Apakah SQL Server memiliki metode pemilihan antara indeks unik dan kunci utama?
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:
ALTER TABLE Child
ADD CONSTRAINT FK_Child_Parent FOREIGN KEY (ParentID)
-- omit key columns of the referenced table
REFERENCES Parent /*(ParentID)*/;
Lebih detail di bawah ini.
Pertimbangkan pengaturan berikut:
CREATE TABLE T (id int NOT NULL, a int, b int, c uniqueidentifier, filler binary(1000));
CREATE TABLE TRef (tid int NULL);
di mana tabel TRef
bermaksud ke tabel referensi T
.
Untuk membuat batasan referensial seseorang dapat menggunakan ALTER TABLE
perintah dengan dua alternatif:
ALTER TABLE TRef
ADD CONSTRAINT FK_TRef_T_1 FOREIGN KEY (tid) REFERENCES T (id);
ALTER TABLE TRef
ADD CONSTRAINT FK_TRef_T_2 FOREIGN KEY (tid) REFERENCES T;
perhatikan bahwa dalam kasus kedua tidak ada kolom dari tabel yang direferensikan ditentukan ( REFERENCES T
versus REFERENCES T (id)
).
Karena belum ada indeks kunci T
, eksekusi perintah ini akan menghasilkan kesalahan.
Perintah pertama mengembalikan kesalahan berikut:
Msg 1776, Level 16, Negara 0, Jalur 4
Tidak ada kunci utama atau kandidat dalam tabel referensi 'T' yang cocok dengan daftar kolom referensi di kunci asing 'FK_TRef_T_1'.
Perintah kedua, bagaimanapun, mengembalikan kesalahan yang berbeda:
Msg 1773, Level 16, Negara 0, Jalur 4
Kunci asing 'FK_TRef_T_2' memiliki referensi implisit ke objek 'T' yang tidak memiliki kunci utama yang ditentukan di atasnya.
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
:
CREATE UNIQUE INDEX IX_T_1 on T(id) INCLUDE (filler);
CREATE UNIQUE INDEX IX_T_2 on T(id) INCLUDE (c);
CREATE UNIQUE INDEX IX_T_3 ON T(id) INCLUDE (a, b);
ALTER TABLE T
ADD CONSTRAINT UQ_T UNIQUE CLUSTERED (id);
perintah untuk FK_TRef_T_1
penciptaan berhasil, tetapi perintah untuk FK_TRef_T_2
penciptaan masih gagal dengan Msg 1773.
Akhirnya, jika kita menambahkan kunci utama pada T
:
ALTER TABLE T
ADD CONSTRAINT PK_T PRIMARY KEY NONCLUSTERED (id);
perintah untuk FK_TRef_T_2
penciptaan berhasil.
Mari kita periksa indeks apa dari tabel T
yang dirujuk oleh kunci asing tabel TRef
:
select
ix.index_id,
ix.name as index_name,
ix.type_desc as index_type_desc,
fk.name as fk_name
from sys.indexes ix
left join sys.foreign_keys fk on
fk.referenced_object_id = ix.object_id
and fk.key_index_id = ix.index_id
and fk.parent_object_id = object_id('TRef')
where ix.object_id = object_id('T');
ini mengembalikan:
index_id index_name index_type_desc fk_name
--------- ----------- ----------------- ------------
1 UQ_T CLUSTERED NULL
2 IX_T_1 NONCLUSTERED FK_TRef_T_1
3 IX_T_2 NONCLUSTERED NULL
4 IX_T_3 NONCLUSTERED NULL
5 PK_T NONCLUSTERED FK_TRef_T_2
lihat yang FK_TRef_T_2
sesuai dengan PK_T
.
Jadi, ya, dengan menggunakan REFERENCES T
sintaks kunci asing TRef
dipetakan ke kunci primer T
.
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
11 Definisi dan manipulasi skema
11.8 <definisi kendala referensial>
Fungsi:
Menentukan batasan referensial.
Format
<referential constraint definition> ::=
FOREIGN KEY <left paren> <referencing columns> <right paren>
<references specification>
<references specification> ::=
REFERENCES <referenced table and columns>
[ MATCH <match type> ]
[ <referential triggered action> ]
...
Aturan Sintaks
...
3) Kasus:
...
b) Jika <tabel dan kolom yang direferensikan> tidak menentukan <daftar kolom referensi>, maka deskriptor tabel dari tabel yang direferensikan harus menyertakan kendala unik yang menentukan KUNCI UTAMA. Biarkan kolom yang direferensikan menjadi kolom atau kolom yang diidentifikasi oleh kolom unik dalam batasan unik tersebut dan biarkan kolom yang direferensikan
menjadi salah satu kolom tersebut. <Tabel dan kolom yang direferensikan> harus dianggap secara implisit menentukan <daftar kolom referensi> yang identik dengan <daftar kolom unik> tersebut.
...
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 yang index_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.