NULL sütununda benzersiz bir dizin nasıl oluşturulur?


101

SQL Server 2005 kullanıyorum. NULLS'a izin verirken bir sütundaki değerleri benzersiz olacak şekilde sınırlamak istiyorum.

Mevcut çözümüm, aşağıdaki gibi bir görünümde benzersiz bir dizin içeriyor:

CREATE VIEW vw_unq WITH SCHEMABINDING AS
    SELECT Column1
      FROM MyTable
     WHERE Column1 IS NOT NULL

CREATE UNIQUE CLUSTERED INDEX unq_idx ON vw_unq (Column1)

Daha iyi bir fikrin var mı?


16
2008 sql kullanma şansı yok mu? 'nerede' seçeneğini kullanarak filtrelenmiş bir dizin oluşturabilirsiniz
Simon_Weaver

3
Eşsiz demek istemediniz , NULL'lara izin vermek , benzersiz kastediyor gibisiniz , ancak birden çok NULL içeriyor . Aksi takdirde, NULL diğer herhangi bir değer gibi dizine alınır ve benzersizlik kısıtlaması beklendiği gibi çalışır - aşağıdaki yorumda belirtildiği gibi @pst gibi SQL standartlarına göre değil.
Suncat2000

Yanıtlar:


26

Uniquelerin amacını ihlal ettiği için bunu yapamayacağınızdan oldukça eminim.

Ancak, bu kişinin etrafında makul bir işi var gibi görünüyor: http://sqlservercodebook.blogspot.com/2008/04/multiple-null-values-in-unique-index-in.html


2
Görünüşe göre, sağladığınız bağlantının içeriği atıf yapılmadan buradan (kısmen) kopyalandı: decipherinfosys.wordpress.com/2007/11/30/…
Tom Juergens

77
Bunun "benzersizlerin amacını ihlal ettiği" konusunda hemfikir değilim - NULL, SQL'de özel bir değerdir (birçok yönden NaN'ye benzer) ve buna göre ele alınması gerekir. Aslında SQL Server'da çeşitli SQL spesifikasyonlarına uymak bir başarısızlıktır: burada, değerine göre "doğru uygulama" için bir istek bağlantısı vardır: connect.microsoft.com/SQLServer/feedback/details/299229/… .

5
2008'de referans için EŞSİZ DİZİN OLUŞTUR foo ON dbo.bar (key) NULL ANAHTAR NULL OLMADIĞINI;
niico

2
Ben de "benzersizlerin amacını ihlal ediyor" konusuna katılmıyorum, NULL, NULL'a eşit değildir, bu nedenle null yapılabilir sütunda benzersiz dizin oluşturabilmeli ve birden çok boş girebilmelisiniz.
Wodzu

105

SQL Server 2008'i kullanarak filtrelenmiş bir dizin oluşturabilirsiniz: http://msdn.microsoft.com/en-us/library/cc280372.aspx . (Simon'un bunu bir yorum olarak eklediğini görüyorum, ancak yorum kolayca gözden kaçtığı için kendi cevabını hak ettiğini düşündüm.)

Diğer bir seçenek, benzersizliği kontrol etmek için bir tetikleyicidir, ancak bu performansı etkileyebilir.


84
create unique index UIX on MyTable (Column1) where Column1 is not null
Jørn Schou-Rode

1
Not: Şu anda SQL Server Management Studio, bu tür dizinlerin nasıl oluşturulacağını bilmiyor gibi görünüyor, bu nedenle tabloyu daha sonra değiştirirseniz kafası karışacak ve onu bırakmaya çalışacak, bu yüzden yeniden oluşturmayı unutmayın
Simon_Weaver

3
Görünüşe göre Microsoft bunu desteklemek için SSMS'yi güncelledi. SSMS 10.50.1617'ye sahibim ve Dizin Özellikleri iletişim kutusunda filtreyi düzenlemek için Filtre sayfasını seçebilirsiniz. örneğin "([Sütun1] BOŞ DEĞİL)"
Phil Haselden

5
Bir dizinde birden çok boşluğa izin vermek ve bir dizinden boş değerleri filtrelemek ayrı şeylerdir. Bir dizine filtre uygulamak aslında kayıtları dizinden hariç tutar, oysa diğer çözümler null değerini kullanışlı benzersiz bir değere dönüştürür. Farkın farkında olun.
Suncat2000

Böyle bir filtre uygulanmış dizin içeren bir tablo saklanan prosedürleri kullanarak iseniz, emin ANSI_NULLSolan ONverileri eklemek çalışırken aksi takdirde bir hata alırsınız.
Arne

71

Hesaplanan sütun numarası, yaygın olarak "sıfır avcısı" olarak bilinir; notlarım Steve Kass'a atıfta bulunuyor:

CREATE TABLE dupNulls (
pk int identity(1,1) primary key,
X  int NULL,
nullbuster as (case when X is null then pk else 0 end),
CONSTRAINT dupNulls_uqX UNIQUE (X,nullbuster)
)

Bu harika bir numara gibi görünüyor. Garip bir şekilde nullbuster'ı aramak çok fazla şey getirmez. Bunun aramaları hızlandırmak için de yararlı olup olmayacağını merak ediyorum - boş için sadece 1 ve 0'ın hesaplanmış bir sütunu yerine, PK'yi kullanmak dizine daha fazla çalışacak bir şey veriyorsa? Bu hafta sonu büyük bir masada test edip göreceğiz.
David Storfer

@DavidStorfer, bunu yapamazsınız çünkü iki farklı tablonun kimlikleri arasında bir çakışma olabilir.
user393274

İyileştirme: ISNULL (X, CONVERT (VARCHAR (10), pk))
Faiz

5
@Faiz: İyileştirme, bakanın gözündedir. Orijinalin görünümünü tercih ederim.
2014

@NunoG, kaybolabilecek harici bir siteyi bağlamak yerine, gereksinimlerinize uygun iyi bir çözüm sağladığı için kabul edilen yanıt bu olmalıdır.
Frédéric

-3

Kesin olarak konuşursak, benzersiz bir boş değer atanabilir sütun (veya sütun kümesi) yalnızca bir kez NULL (veya bir NULL kaydı) olabilir, çünkü aynı değere (ve bu NULL içerir) birden fazla kez benzersiz kısıtlamayı açıkça ihlal eder.

Ancak bu, "benzersiz boş değer atanabilir sütunlar" kavramının geçerli olduğu anlamına gelmez; bunu herhangi bir ilişkisel veritabanında gerçekten uygulamak için, bu tür veritabanlarının düzgün çalışması için normalleştirilmesi gerektiğini ve normalizasyonun genellikle varlıklar arasında ilişkiler kurmak için birkaç (varlık olmayan) ekstra tablonun eklenmesini içerdiğini unutmamalıyız. .

Yalnızca bir "benzersiz boş değer atanabilir sütun" göz önünde bulundurularak temel bir örnek üzerinde çalışalım, onu bu tür daha fazla sütuna genişletmek kolaydır.

Şöyle bir tabloyla temsil edilen bilgileri varsayalım:

create table the_entity_incorrect
(
  id integer,
  uniqnull integer null, /* we want this to be "unique and nullable" */
  primary key (id)
);

Bunu uniqnull'u ayırarak ve uniqnull değerleri ile the_entity arasında bir ilişki kurmak için ikinci bir tablo ekleyerek yapabiliriz (uniqnull "the_entity" içinde "yerine):

create table the_entity
(
  id integer,
  primary key(id)
);

create table the_relation
(
  the_entity_id integer not null,
  uniqnull integer not null,

  unique(the_entity_id),
  unique(uniqnull),
  /* primary key can be both or either of the_entity_id or uniqnull */
  primary key (the_entity_id, uniqnull), 
  foreign key (the_entity_id) references the_entity(id)
);

Bir uniqnull değerini bir satırla ilişkilendirmek için, ilişkiye bir satır da eklememiz gerekir.

Özgünlükteki satırlar için hiçbir benzersiz değer ilişkilendirilmedi (yani, özdeşlik_ yanlışına NULL koyacağımız olanlar için) ilişkiye bir satır eklemiyoruz.

Uniqnull için değerlerin tüm the_relation için benzersiz olacağına dikkat edin ve ayrıca, the_entity'deki her bir değer için, üzerindeki birincil ve yabancı anahtarlar bunu zorladığından, the_relation'da en fazla bir değer olabileceğine dikkat edin.

Daha sonra, uniqnull için 5 değeri, 3 olan bir the_entity id ile ilişkilendirilecekse, şunları yapmamız gerekir:

start transaction;
insert into the_entity (id) values (3); 
insert into the_relation (the_entity_id, uniqnull) values (3, 5);
commit;

Ve, eğer the_entity için 10'luk bir id değerinin benzersiz karşılığı yoksa, yalnızca şunları yaparız:

start transaction;
insert into the_entity (id) values (10); 
commit;

Bu bilgiyi normalden arındırmak ve verileri_entity_incorrect gibi bir tablonun tutacağı verileri elde etmek için yapmamız gerekenler:

select
  id, uniqnull
from
  the_entity left outer join the_relation
on
  the_entity.id = the_relation.the_entity_id
;

"Sol dış birleştirme" operatörü, the_entity'deki tüm satırların sonuçta görünmesini sağlar ve ilişkide eşleşen sütun bulunmadığında uniqnull sütununa NULL koyar.

İyi normalleştirilmiş bir veritabanı (ve buna karşılık gelen normalleştirici görünümler ve prosedürler) tasarlamak için bazı günler (veya haftalar veya aylar) için harcanan herhangi bir çaba, sizi yıllarca (veya on yıllarca) acıdan ve boşa harcanan kaynaklardan kurtaracaktır.


6
Kabul edilen yanıtçının elli ek oy içeren yorumunda daha önce belirtildiği gibi, MS Sql Sunucusu tarafından benzersiz olarak endekslenen bir sütunda birden fazla boş değer olması için desteklenmelidir. Buna izin vermemek SQL standartlarının uygulanmamasıdır. Null bir değer değildir, null null'a eşit değildir, yani yıllardan beri temel bir SQL kuralıdır. Yani ilk cümlenin yanlış ve çoğu okuyucu okumaya zahmet etmeyecek.
Frédéric
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.