SQL Server 2008 Dan Ke Atas
Cukup filter indeks unik:
CREATE UNIQUE NONCLUSTERED INDEX UQ_Party_SamAccountName
ON dbo.Party(SamAccountName)
WHERE SamAccountName IS NOT NULL;
Dalam Versi Lebih Rendah, Tampilan Terwujud Masih Tidak Diperlukan
Untuk SQL Server 2005 dan sebelumnya, Anda bisa melakukannya tanpa melihat. Saya baru saja menambahkan batasan unik seperti yang Anda minta ke salah satu meja saya. Mengingat bahwa saya ingin keunikan dalam kolom SamAccountName
, tetapi saya ingin memperbolehkan beberapa NULL, saya menggunakan kolom terwujud daripada tampilan terwujud:
ALTER TABLE dbo.Party ADD SamAccountNameUnique
AS (Coalesce(SamAccountName, Convert(varchar(11), PartyID)))
ALTER TABLE dbo.Party ADD CONSTRAINT UQ_Party_SamAccountName
UNIQUE (SamAccountNameUnique)
Anda hanya perlu meletakkan sesuatu di kolom yang dihitung yang akan dijamin unik di seluruh tabel ketika kolom unik yang sebenarnya diinginkan adalah NULL. Dalam hal ini, PartyID
adalah kolom identitas dan menjadi numerik tidak akan pernah cocok dengan itu SamAccountName
, jadi itu berfungsi untuk saya. Anda dapat mencoba metode Anda sendiri — pastikan Anda memahami domain data Anda sehingga tidak ada kemungkinan persimpangan dengan data nyata. Itu bisa sesederhana menambahkan karakter pembeda seperti ini:
Coalesce('n' + SamAccountName, 'p' + Convert(varchar(11), PartyID))
Bahkan jika PartyID
suatu hari menjadi non-numerik dan bisa bertepatan dengan SamAccountName
, sekarang tidak masalah.
Perhatikan bahwa keberadaan indeks termasuk kolom yang dihitung secara implisit menyebabkan setiap hasil ekspresi disimpan ke disk dengan data lain dalam tabel, yang TIDAK mengambil ruang disk tambahan.
Perhatikan bahwa jika Anda tidak ingin indeks, Anda masih dapat menyimpan CPU dengan membuat ekspresi menjadi prakalkulasi ke disk dengan menambahkan kata kunci PERSISTED
ke akhir definisi ekspresi kolom.
Di SQL Server 2008 dan lebih tinggi, pasti gunakan solusi yang difilter jika Anda bisa!
Kontroversi
Harap dicatat bahwa beberapa profesional basis data akan melihat ini sebagai kasus "pengganti NULLs", yang pasti memiliki masalah (sebagian besar disebabkan oleh masalah seputar upaya menentukan kapan sesuatu bernilai nyata atau nilai pengganti untuk data yang hilang) ; ada juga masalah dengan jumlah nilai pengganti non-NULL yang berlipat ganda seperti orang gila).
Namun, saya percaya kasus ini berbeda. Kolom yang dihitung yang saya tambahkan tidak akan pernah digunakan untuk menentukan apa pun. Itu tidak memiliki arti itu sendiri, dan mengkodekan tidak ada informasi yang belum ditemukan secara terpisah di kolom lain yang didefinisikan dengan benar. Seharusnya tidak pernah dipilih atau digunakan.
Jadi, cerita saya adalah bahwa ini bukan NULL pengganti, dan saya berpegang teguh pada itu! Karena kita sebenarnya tidak ingin nilai non-NULL untuk tujuan apa pun selain untuk menipuUNIQUE
indeks agar mengabaikan , kasus penggunaan kami tidak memiliki masalah yang muncul dengan pembuatan NULL pengganti yang normal.
Semua yang dikatakan, saya tidak punya masalah dengan menggunakan tampilan yang diindeks sebagai gantinya — tetapi itu membawa beberapa masalah dengan itu seperti persyaratan menggunakan SCHEMABINDING
. Bersenang-senang menambahkan kolom baru ke tabel basis Anda (Anda minimal harus menjatuhkan indeks, dan kemudian meninggalkan tampilan atau mengubah tampilan agar tidak terikat skema). Lihat daftar lengkap (panjang) persyaratan untuk membuat tampilan yang diindeks di SQL Server (2005) (juga versi yang lebih baru), (2000) .
Memperbarui
Jika kolom Anda numerik, mungkin ada tantangan untuk memastikan bahwa penggunaan kendala unik Coalesce
tidak menghasilkan tabrakan. Dalam hal ini, ada beberapa opsi. Satu mungkin menggunakan angka negatif, untuk menempatkan "pengganti NULLs" hanya di kisaran negatif, dan "nilai nyata" hanya di kisaran positif. Sebagai alternatif, pola berikut dapat digunakan. Dalam tabel Issue
(di mana IssueID
ada PRIMARY KEY
), mungkin ada atau tidak ada TicketID
, tetapi jika ada, itu harus unik.
ALTER TABLE dbo.Issue ADD TicketUnique
AS (CASE WHEN TicketID IS NULL THEN IssueID END);
ALTER TABLE dbo.Issue ADD CONSTRAINT UQ_Issue_Ticket_AllowNull
UNIQUE (TicketID, TicketUnique);
Jika IssueID 1 memiliki tiket 123, batasannya UNIQUE
adalah nilai (123, NULL). Jika IssueID 2 tidak memiliki tiket, tiket tersebut akan aktif (NULL, 2). Beberapa pemikiran akan menunjukkan bahwa batasan ini tidak dapat diduplikasi untuk setiap baris dalam tabel, dan masih memungkinkan beberapa NULL.