UNIQUE kısıtlaması neden sadece bir NULL'a izin veriyor?


36

Teknik olarak, NULL = NULL yanlıştır, bu mantıkla hiçbir NULL herhangi bir NULL'a eşittir ve tüm NULL'ler farklıdır. Bu, tüm NULL'lerin benzersiz olduğu ve benzersiz bir dizinin herhangi bir sayıda NULL'a izin vermesi gerektiği anlamına gelmez mi?


Yorumlar uzun tartışmalar için değildir; bu konuşma sohbete taşındı .
Paul White GoFundMonica

Yanıtlar:


52

Neden bu şekilde çalışıyor? Çünkü bir zamanlar, standardın ne dediğini bilmeden veya önemsemeden bir tasarım kararı verdi (sonuçta, NULLs ile her türlü garip davranışa sahibiz ve istediklerinde farklı davranışları zorlayabilir). Bu karar, bu durumda, dikte etti NULL = NULL.

Çok akıllıca bir karar değildi. Yapmaları gereken, varsayılan davranışın ANSI standardına uymasıdır ve gerçekten bu tuhaf davranışı istiyorlarsa, WITH CONSIDER_NULLS_EQUALya da gibi bir DDL seçeneğine izin verin WITH ALLOW_ONLY_ONE_NULL.

Elbette, görüş açısı 20/20.

Her neyse, en temiz ya da sezgisel olmasa bile, bir çözümümüz var.

Eşsiz, filtrelenmiş bir dizin oluşturarak, uygun ANSI davranışını SQL Server 2008 ve üzeri sürümlerde alabilirsiniz.

CREATE UNIQUE INDEX foo ON dbo.bar(key) WHERE key IS NOT NULL;

Bu, birden fazla NULLdeğere izin verir, çünkü bu satırlar yinelenen denetlemenin dışında bırakılır. Ek bir bonus olarak, bu, birden fazla NULLs'ye izin verildiyse tüm tablonun oluşturduğundan daha küçük bir endeks olarak sonuçlanır (özellikle endekste tek sütun olmadığında, INCLUDEsütunlara vb. Sahip). Bununla birlikte, filtre uygulanmış dizinlerin diğer sınırlamalarından bazılarının farkında olmak isteyebilirsiniz:


8

Doğru. Sql sunucusunda benzersiz bir kısıtlama veya dizinin uygulanması bir ve sadece bir NULL'a izin verir. Ayrıca, teknik olarak NULL tanımına uymadığını doğrulayın, ancak "teknik olarak" doğru olmasa da, onu daha kullanışlı hale getirmek için yaptıkları şeylerden biri. Bir PRIMARY KEY (ayrıca benzersiz bir dizin) NULL'lara izin vermez (elbette).


1
Bu (SQL Server'ın) teknikliği de SQL standardına uymuyor. Bu konuda 7 yaşındaki bir Connect ürünü var.
ypercubeᵀᴹ

@ypercube Doğru. Bu yüzden bunun sadece bir uygulama olduğunu ve NULL tanımına tam olarak uymadığını söyledim. Filtrelenmiş benzersiz dizini düşünmedim (başka şeyler için de kullandım.)
Kenneth Fisher

3

İlk önce - "Null value" cümlesini kullanmayı bırak, sadece seni yanıltmaya götürür. Bunun yerine, "null marker" (boş işaretçi) ifadesini kullanın - bu sütundaki gerçek değerin eksik veya uygulanamaz olduğunu belirten bir sütundaki işaretleyici kullanın (ancak, işaretleyicinin bu seçeneklerden hangisinin aslında böyle olduğunu söylemediğini not edin).

Şimdi, aşağıdakileri hayal edin (veritabanının modellenen durum hakkında tam bir bilgiye sahip olmadığı).

Situation          Database

ID   Code          ID   Code
--   -----         --   -----
1    A             1    A
2    B             2    (null)
3    C             3    C
4    B             4    (null)

Modellemekte olduğumuz bütünlük kuralı "Kod benzersiz olmalı" dır. Gerçek dünyadaki durum bunu ihlal ediyor, bu yüzden veritabanı hem 2. hem de 4. öğenin aynı anda masada olmasına izin vermiyor.

En güvenli ve en az esnek olan yaklaşım, Kod alanındaki boş işaretlere izin vermemektir, bu nedenle tutarsız veri olasılığı yoktur. En esnek yaklaşım, birden fazla boş işaretçiye izin vermek ve değerler girildiğinde benzersiz olma konusunda endişelenmek olacaktır.

Sybase programcıları, masaya yalnızca bir boş işaretçi koymaya izin veren biraz güvenli, esnek olmayan bir yaklaşıma gittiler - yorumcular bir o zamandan beri şikayet ediyorlardı. Microsoft bu davranışı sürdürdü, geriye dönük uyumluluk için sanırım.


D Codd'ün biri boş, diğeri uygulanamaz olan - iki boş işaretleyici uygulamayı düşündüğünü düşündüğüm bir yerde okudum, ancak reddetti, ancak referansı bulamıyorum. Doğru hatırlıyor muyum?

PS Null hakkında en sevdiğim alıntı: Louis Davidson, "Professional SQL Server 2000 Veri Tabanı Tasarımı", Wrox Press, 2001, sayfa 52. "Tek bir cümleye kadar kaynatıldı: NULL kötüdür."


1
Tek bir kişiye izin vermek de nullbu amacı gerçekleştiremez. Çünkü eksik değer, diğer satırlardan birindeki değerle aynı olabilir.
Martin Smith

1
MartinSmith'in söylediği. Ya bir kontrol kısıtlamanız varsa CHECK (Value IN ('A','B','C','D'))? Daha sonra hem SQL-Server'ın uygulaması hem de SQL standardı tablonun 5 satır olmasına izin verir (her değer için bir satır artı NULL ile 1). Ardından, tartışmalı bir şekilde, veritabanı sınırlamaları ile tutarlı olsa da, tasarımcının amacı ile tutarlı değildir. Tabloda en fazla 4 satır olması. Bir veya daha fazla satır silinmedikçe, NULL'un bir kısıtlamayı ihlal etmeyecek şekilde değiştirilebileceği bir değer yoktur.
ypercubeᵀᴹ

1
Standardın 5 yerine 6 hatta 106 satıra izin vermesi, bu senaryoda her ikisinin de başarısız olacağını değiştirmez.
ypercubeᵀᴹ

@ Martin Smith, olabilir, ama daha sonra yine olmayabilir - veritabanı sunucusu söyleyemez, bu yüzden risk almaz ve güvenli rota izler. Sybase'in (tahminimce) programcıların karar verdikleri karar buydu, o zamandan beri sıkıntıya neden oldu (en azından kitaplıktaki en eski kitap olan Inside SQL Server 6.5 kadar, Ron Soukup'un Aaron Bertrand'ın cevabında yaptığı aynı yorumu yaptığı) . Daha kötüsü olabilir sanırım - boş belirteçlere mecbur olamazlardı. :-)
Greenstone Walker

2
@GreenstoneWalker - "Güvenli" rotayı kullanmaz. Eksik değerin çatışmayacağını varsayar. CREATE TABLE #T(A INT NULL UNIQUE);INSERT INTO #T VALUES (1),(NULL);UPDATE #T SET A = 1 WHERE A IS NULL;bir hataya yol açacaktır. Tasarım motivasyonlarına ilişkin teorinize göre NULL, ilk duruma eklemeyi engellemesi gerekirdi - çünkü eksik bilgi, değerin farklı olduğuna dair hiçbir garanti olmadığı anlamına gelir.
Martin Smith

2

Bu teknik olarak doğru olmayabilir, ama felsefi olarak geceleri uyumama yardımcı oluyor.

Diğer bazılarının söylediği veya söylediği gibi, NULL'yi bilinmeyen olarak düşünüyorsanız, bir NULL değerinin aslında bir NULL değerine eşit olup olmadığını belirleyemezsiniz. Bu şekilde düşünerek, NULL == NULL ifadesi, NULL olarak değerlendirmeli, yani bilinmeyen anlamına gelmelidir.

Benzersiz bir kısıtlamanın, sütun değerlerinin karşılaştırılması için kesin bir değere ihtiyacı olacaktır. Başka bir deyişle, eşitlik operatörünü kullanarak tek bir sütun değerini diğer sütun değerleriyle karşılaştırırken, geçerli olduğunu yanlış olarak değerlendirmek gerekir. Bilinmeyen, çoğu zaman sahte olarak kabul edilse de, yanlış değildir. İki NULL değer eşit olabilir veya olmayabilir ... kesin olarak belirlenemez.

Eşsiz bir kısıtlamanın, birbirinden farklı olduğu belirlenen değerleri kısıtlamak olarak düşünmesine yardımcı olur. Bununla demek istediğim şuna benzeyen bir SELECT çalıştırıyorsanız:

SELECT * from dbo.table1 WHERE ColumnWithUniqueContraint="some value"

Çoğu insan, benzersiz bir kısıtlama olduğu göz önüne alındığında bir sonuç bekler. ColumnWithUniqueConstraint öğesinde birden fazla NULL değerine izin vermişseniz, karşılaştırılmış değer olarak NULL kullanarak tablodan tek bir ayrı satır seçmek mümkün olmaz.

Buna göre, NULL tanımına göre doğru uygulanıp uygulanmadığına bakılmaksızın, çoğu durumda kesinlikle NULL değerlerine izin vermekten çok daha pratik olduğuna inanıyorum.


Seçiminiz, Benzersiz bir kısıtlama olduğunda (herhangi bir uygulamada, yalnızca SQL Server değil) 1 sonuç verir. Amacın ne?
ypercubeᵀᴹ

-3

Bir UNIQUEkısıtlamanın ana amaçlarından biri yinelenen kayıtları önlemektir. Bir değerin "bilinmeyen" olduğu birden fazla kaydın olabileceği bir tablonun olması gerekiyorsa, ancak aynı "bilinen" değere sahip iki kaydın bulunmasına izin verilmiyorsa, o zaman bilinmeyen değerlere daha önce yapay benzersiz tanımlayıcılar atanmalıdır. masaya eklendi.

Bir UNIQUEkısıtlı olan ve tek bir boş değer içeren bir sütunun nadir olduğu birkaç durum vardır ; Örneğin, bir tablo sütun değerleri ve yerelleştirilmiş metin açıklamaları arasında bir eşleme içeriyorsa, bir satır için bir satır, NULLbaşka bir tablodaki o sütun olduğunda görünmesi gereken tanımlamayı tanımlamayı mümkün kılar NULL. NULLKullanım durumu için izin verme davranışı .

Aksi takdirde, UNIQUEbirçok özdeş kaydın varlığına izin vermek için herhangi bir sütunda kısıtlı bir veritabanı için bir temel göremiyorum , ancak anahtar değerleri ayırt edilemeyen birden fazla kayda izin verirken bunu önlemenin bir yolunu görmüyorum. Bunun NULLkendisine eşit olmadığını beyan etmek NULLdeğerleri birbirinden ayırt etmeyecektir .


3
Yapay benzersiz tanımlayıcılar bir şaka, üzgünüm. Bir VIN için bunu nasıl yapacaksın? Ne olduğunu bilmiyorsan, neden bir şeyler uydurdun? Sadece fazladan disk alanı kaplamak için mi? Başka bir problemi çözmek için saçma sapan gibi görünüyor (uygulamayı NULL'ları ustaca kullanacak şekilde yazmak istememek gibi). Bir şeyin neden NULL olduğunu kesinlikle bilmeniz gerekiyorsa (var ancak bilinmediği vs. var olmadığını biliyorum - bilmediğiniz veya umursamıyorsanız, örneğin) bir tür durum sütunu ekleyin. Jetonlar sadece onlarla başa çıkmak için garip kandırma koduna yol açar.
Aaron Bertrand

Çok şey, benzersiz kısıtlamanın amacına bağlıdır. Bir alan tanımlayıcı olarak kullanılacaksa, boş olmamalıdır. İş kurallarının, bir öğenin iki kez görünmesi durumunda, bunlardan birinin yanlış olması gerektiğini, ancak bazı öğelerin "bilmeme" olabileceğini önerdiği durumlarda, benzersiz bir kısıtlama, uygun bir yaklaşım gibi hissetmez. Birinin bilinen bir VIN'e sahip bir aracı varsa ve veritabanında diğeriyle çakışıyorsa, VIN'lerden en az birinin yanlış olduğunu biliyor olabilir, ancak veritabanının her iki kayıt için de inanılan değeri tahmin etmekten daha iyi rapor edebileceğini bilmek iyi olabilir bu doğru.
supercat

@AaronBertrand: Muhtemelen boş bir benzersiz-eğer-olmayan-boş bir alanın bir alan anahtarı olması gerekebileceği-bazı durumlarda, alanı doldurmadan önce (örneğin, "eş kimliği"), "benzersiz" bir kısıtlamanın yetersiz olacağı; X.Spouse boş değilse, X.Spouse.Spouse = X olması gerekir. Bu arada, "eş" gibi bir şey de evlenmemiş bir kişinin sicilinin eş olarak "NULL" a sahip olmaması gerektiğini, bunun yerine X.spouse.spouse = X kuralının kendi kimliğini göstermesi gerektiğini söyleyerek ele alınabilir. herkese uygulayın.
supercat
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.