Kapan saya harus menggunakan batasan unik alih-alih indeks unik?

195

Ketika saya ingin kolom memiliki nilai yang berbeda, saya bisa menggunakan batasan

create table t1(
id int primary key,
code varchar(10) unique NULL
);
go

atau saya bisa menggunakan indeks unik

create table t2(
id int primary key,
code varchar(10) NULL
);
go

create unique index I_t2 on t2(code);

Kolom dengan batasan unik tampaknya menjadi kandidat yang baik untuk indeks unik.

Adakah alasan yang diketahui untuk menggunakan batasan unik dan bukan untuk menggunakan indeks unik saja?

bernd_k
sumber
9
apakah mereka sebenarnya berbeda? Saya pikir dalam beberapa database misalnya postgresql, batasan unik hanya membuat indeks unik. Saya tidak menjawab karena saya tidak tahu apa-apa tentang sql server.
xenoterracide
6
di postgresql, Anda bisa menggunakan ekspresi dalam indeks unik tetapi tidak dalam kendala unik.
Neil McGuigan
1
Pada MS SQL, mereka menerapkan hal yang sama. Coba buat dua tabel dengan data yang sama, satu dengan kendala unik, yang lain dengan indeks unik. Mereka akan menggunakan jumlah ruang indeks yang sama, dan keduanya akan dapat mencari terhadap indeks unik yang (dalam praktiknya) dibuat dengan cara apa pun.
Jon dari Semua Perdagangan

Jawaban:

153

Di bawah tenda, batasan unik diimplementasikan dengan cara yang sama dengan indeks unik - indeks diperlukan untuk secara efisien memenuhi persyaratan untuk menegakkan batasan. Bahkan jika indeks dibuat sebagai hasil dari batasan UNIK, perencana kueri dapat menggunakannya seperti indeks lainnya jika melihatnya sebagai cara terbaik untuk mendekati kueri yang diberikan.

Jadi untuk database yang mendukung kedua fitur pilihan yang akan digunakan akan sering turun ke gaya dan konsistensi yang disukai.

Jika Anda berencana untuk menggunakan indeks sebagai indeks (mis. Kode Anda mungkin mengandalkan pencarian / pengurutan / pemfilteran pada bidang itu agar lebih cepat) Saya akan secara eksplisit menggunakan indeks yang unik (dan mengomentari sumbernya) daripada menjadi kendala untuk membuatnya jelas - dengan cara ini jika persyaratan keunikan diubah dalam revisi nanti dari aplikasi yang Anda (atau coder lain) akan tahu untuk memastikan indeks non-unik diletakkan di tempat yang unik (hanya menghapus kendala unik akan menghapus indeks sepenuhnya). Juga indeks tertentu dapat dinamai dalam petunjuk indeks (yaitu DENGAN (INDEX (ix_index_name)), yang menurut saya bukan kasus untuk indeks yang dibuat di belakang layar untuk mengelola keunikan karena Anda tidak mungkin tahu namanya.

Demikian juga jika Anda hanya perlu menegakkan keunikan sebagai aturan bisnis daripada bidang yang perlu dicari atau digunakan untuk menyortir maka saya akan menggunakan kendala, lagi untuk membuat penggunaan yang dimaksudkan lebih jelas ketika orang lain melihat definisi tabel Anda.

Perhatikan bahwa jika Anda menggunakan batasan unik dan indeks unik pada bidang yang sama, basis data tidak akan cukup cerah untuk melihat duplikasi, sehingga Anda akan berakhir dengan dua indeks yang akan menggunakan ruang ekstra dan memperlambat sisipan / pembaruan baris.

David Spillett
sumber
1
Saya bertanya-tanya tentang "database tidak akan cukup cerah"? Apakah itu berlaku untuk semua RDBMS? Apakah ini diamanatkan oleh SQL-Standard? Dan bahkan jika itu (dan saya ingin tahu mengapa harus demikian), apakah semua implementasi mengimplementasikannya seperti itu? Atau: mengapa DB tidak cukup "cerah"?
Jürgen A. Erhard
4
@jae: DBMS tentu bisa cukup cerah, tetapi Anda harus memeriksa dengan masing-masing DBMS untuk melihat apakah itu. Jika Anda meminta MSSQL untuk membuat dua indeks identik, itu akan membuat dua daripada satu disebut dengan dua nama (setidaknya ini adalah kasus terakhir kali saya melihat situasi seperti itu (karena kesalahan copy + paste pada bagian saya)), jadi saya berasumsi hal yang sama terjadi jika salah satu indeks hadir karena kendala.
David Spillett
3
+1 @ David Spillett Saya pikir pada dasarnya DBMS hanya mengasumsikan Anda tahu apa yang Anda lakukan; Jika Anda merasa ingin membuat indeks yang sama dua kali, itu tidak mempertanyakan Anda tentang itu.
Andrew Barber
2
sangat mendalam. Apakah Anda tahu kalau perilaku ini ada di MySQL dan Apache Derby juga?
corsiKa
5
Anda bisa memberi nama batasan dan menggunakannya dalam petunjuk indeks. CREATE TABLE #T(X INT CONSTRAINT PK PRIMARY KEY NONCLUSTERED);SELECT * FROM #T WITH(INDEX(PK)) WHERE X = 1. Indeks dapat lebih fleksibel meskipun dalam hal kendala tidak mendukung semua opsi indeks seperti INCLUDEd kolom atau indeks yang difilter.
Martin Smith
101

Selain poin dalam jawaban lain, berikut adalah beberapa perbedaan utama antara keduanya.

Catatan: Pesan kesalahan berasal dari SQL Server 2012.

Kesalahan

Pelanggaran galat pengembalian kendala unik 2627.

Msg 2627, Level 14, State 1, Line 1
Violation of UNIQUE KEY constraint 'P1U_pk'. Cannot insert duplicate key in object 'dbo.P1U'. The duplicate key value is (1).
The statement has been terminated.

Pelanggaran kesalahan pengembalian indeks unik 2601.

Msg 2601, Level 14, State 1, Line 1
Cannot insert duplicate key row in object 'dbo.P1' with unique index 'P1_u'. The duplicate key value is (1).
The statement has been terminated.

Menonaktifkan

Batasan unik tidak dapat dinonaktifkan.

Msg 11415, Level 16, State 1, Line 1
Object 'P1U_pk' cannot be disabled or enabled. This action applies only to foreign key and check constraints.
Msg 4916, Level 16, State 0, Line 1
Could not enable or disable the constraint. See previous errors.

Tetapi indeks unik di belakang batasan kunci primer atau batasan unik dapat dinonaktifkan, seperti halnya indeks unik apa pun. Hat-tip Brain2000.

ALTER INDEX P1_u ON dbo.P1 DISABLE ;

Perhatikan peringatan biasa bahwa menonaktifkan indeks yang dikelompokkan membuat data tidak dapat diakses.

Pilihan

Kendala unik mendukung opsi pengindeksan seperti FILLFACTORdan IGNORE_DUP_KEY, meskipun ini tidak berlaku untuk setiap versi SQL Server.

Termasuk Kolom

Indeks nonclustered dapat mencakup kolom yang tidak diindeks (disebut indeks penutup, ini adalah peningkatan kinerja utama). Indeks di balik kendala PRIMARY KEY dan UNIQUE tidak dapat menyertakan kolom. Hat-tip @ ypercube.

Penyaringan

Batasan unik tidak dapat difilter.

Indeks unik dapat difilter.

CREATE UNIQUE NONCLUSTERED INDEX Students6_DrivesLicence_u 
ON dbo.Students6( DriversLicenceNo ) WHERE DriversLicenceNo is not null ;

Batasan Kunci Asing

Batasan kunci asing tidak dapat referensi indeks unik difilter, meskipun itu dapat referensi indeks unik non-terfilter (saya pikir ini ditambahkan dalam SQL Server 2005).

Penamaan

Saat membuat kendala, menentukan nama kendala adalah opsional (untuk semua lima jenis kendala). Jika Anda tidak menentukan nama maka MSSQL akan menghasilkan satu untuk Anda.

CREATE TABLE dbo.T1 (
    TID int not null PRIMARY KEY
) ;
GO
CREATE TABLE dbo.T2 (
    TID int not null CONSTRAINT T2_pk PRIMARY KEY
) ;

Saat membuat indeks, Anda harus menentukan nama.

Hat-tip @ i-one.

Tautan

http://technet.microsoft.com/en-us/library/aa224827(v=SQL.80).aspx

http://technet.microsoft.com/en-us/library/ms177456.aspx

Greenstone Walker
sumber
Batasan unik dapat dinonaktifkan dan diaktifkan melalui metode yang sama dengan indeks: ALTER INDEX tbl ON uconstraint DISABLE, ALTER INDEX tbl ON uconstraint REBUILD
Brain2000
Terima kasih @ Brain2000. Secara kebetulan, saya mengajar bagian tentang menonaktifkan indeks pagi ini sebelum saya membaca komentar ini.
Greenstone Walker
10

Mengutip MSDN sebagai sumber resmi:

Tidak ada perbedaan yang signifikan antara membuat batasan UNIK dan membuat indeks unik yang independen terhadap kendala . Validasi data terjadi dengan cara yang sama, dan pengoptimal kueri tidak membedakan antara indeks unik yang dibuat oleh kendala atau dibuat secara manual. Namun, membuat batasan UNIK pada kolom membuat tujuan indeks jelas ... info lebih lanjut di sini

Dan...

Mesin Database secara otomatis membuat indeks UNIK untuk menegakkan persyaratan keunikan kendala UNIK. Oleh karena itu, jika upaya untuk memasukkan baris duplikat dibuat, Mesin Database mengembalikan pesan kesalahan yang menyatakan kendala UNIQUE telah dilanggar dan tidak menambahkan baris ke tabel. Kecuali jika indeks yang dikelompokkan secara eksplisit ditentukan, indeks yang unik dan tidak tercakup dibuat secara default untuk menegakkan batasan UNIK ... info lebih lanjut di sini

Lainnya ke: https://technet.microsoft.com/en-us/library/aa224827%28v=sql.80%29.aspx

pengguna919426
sumber
6

Salah satu perbedaan utama antara batasan unik dan indeks unik adalah bahwa batasan kunci asing pada tabel lain dapat mereferensikan kolom yang membentuk batasan unik. Ini tidak benar untuk indeks unik. Selain itu, batasan unik didefinisikan sebagai bagian dari standar ANSI, sedangkan indeks tidak. Akhirnya, kendala unik di anggap tinggal di ranah desain basis data logis (yang mungkin diimplementasikan secara berbeda oleh mesin DB yang berbeda) sementara indeks adalah aspek fisik. Oleh karena itu, kendala unik lebih bersifat deklaratif. Saya lebih suka kendala unik di hampir semua kasus.

Dmitry Frenkel
sumber
8
-1 Dalam SQL Server hal berikut ini salah: "batasan kunci asing pada tabel lain dapat mereferensikan kolom yang membentuk batasan unik. Ini tidak benar untuk indeks unik". Dalam SQL Server, kita bisa merujuk batasan FK ke indeks unik.
AK
4
Kemampuan untuk batasan kunci asing untuk referensi indeks yang unik, saya pikir, ditambahkan dalam SQL Server 2005. Banyak sumber, termasuk beberapa halaman di BOL, belum diperbarui untuk mencerminkan perubahan, jadi saya tidak berpikir jawaban Dmitry layak menerima downvotes. Sisa jawabannya adalah tepat - Kendala adalah standar ANSI, indeks tidak.
Greenstone Walker
meskipun ini downvotes jawaban favorit saya.
miracle173
Standar itu penting. Jika standar Ansi adalah untuk menggunakan Batasan Unik, maka kita harus menggunakan Batasan Unik.
Rhyous
1

Dalam Oracle perbedaan utama adalah Anda dapat membuat indeks fungsi-unik, yang tidak dapat dilakukan dengan kendala unik:

Sebagai contoh

create unique index ux_test on my_table (case when amount != 0 then fk_xyz end);

Jadi fk_xyzhanya unik untuk record yang dimiliki amount != 0.

Amir Pashazadeh
sumber
7
Dalam SQL Server (tag pertanyaan), indeks dapat difilter dengan WHEREklausa. CREATE UNIQUE NONCLUSTERED INDEX P4_U ON DBO.P4 ( PID ) WHERE TXT = 'qwert' ;
Greenstone Walker
-3

Batasan UNIK lebih disukai daripada Indeks UNIK. Ketika kendala tidak unik, Anda perlu menggunakan indeks biasa atau tidak unik. Batasan juga merupakan jenis indeks lainnya. Indeks digunakan untuk akses yang lebih cepat.

Indeks unik dapat memiliki klausa mana. Misalnya, Anda dapat membuat indeks untuk setiap tahun berdasarkan kolom tanggal

WHERE Sale_Date BETWEEN '2012-01-01' AND '2012-12-31'
Ravi Ramaswamy
sumber
Bagus untuk melihat manfaat klausa mana yang disebutkan.
crokusek
3
"Kendala juga merupakan jenis indeks lain." Tidak. Beberapa kendala (PK, UQ, FK) dapat dan sering ditegakkan dengan menggunakan indeks. Namun tidak harus dan tidak secara default di semua DBMS.
ypercubeᵀᴹ