Mevcut ihlalleri görmezden gelen benzersiz bir kısıtlama ekleyebilir miyim?


39

Şu anda bir sütunda yinelenen değerleri olan bir tablo var.

Bu hatalı kopyaları çıkaramıyorum ancak benzersiz olmayan ek değerlerin eklenmesini önlemek istiyorum.

UNIQUEMevcut uygunluğu kontrol etmeyen bir şey oluşturabilir miyim ?

Kullanmayı denedim NOCHECKama başarısız oldu.

Bu durumda, lisans bilgilerini "ŞirketAdı" olarak bağlayan bir masam var.

EDIT: Aynı "CompanyName" ile birden fazla satır olması kötü bir veridir, ancak şu anda bu kopyaları çıkaramıyor veya güncelleyemiyoruz. Yaklaşımlardan biri, INSERTyinelenenler için başarısız olacak bir saklı yordam kullanmaktır.

Bu veriler şirket adına göre sorgulandı. Mevcut birkaç kopya için bu, birden fazla satırın döndürülmesi ve gösterilmesi anlamına gelecektir ... Bu yanlış olsa da, kullanım durumumuzda kabul edilebilir. Amaç gelecekte onu önlemektir. Bana göre bu mantığı saklı yordamlarda yapmak zorundayım.


Tabloyu değiştirmenize izin verilir mi (bir sütun daha ekleyin)?
ypercubeᵀᴹ

@ypercube ne yazık ki değil.
Matthew,

Yanıtlar:


33

Cevap Evet". Bunu filtrelenmiş bir indeks ile yapabilirsiniz ( belgeler için buraya bakınız ).

Örneğin, şunları yapabilirsiniz:

create unique index t_col on t(col) where id > 1000;

Bu , eski satırlardan ziyade yalnızca yeni satırlarda benzersiz bir dizin oluşturur . Bu özel formülasyon, mevcut değerlere sahip kopyalara izin verecektir.

Eğer sadece bir avuç çift kopya varsa, şöyle bir şey yapabilirsin:

create unique index t_col on t(col) where id not in (<list of ids for duplicate values here>);

2
Bunun iyi olup olmadığına, "eski" mevcut öğelerin aynı değere sahip yeni öğeler oluşturulmasını engelleyip engellememesine bağlı olacaktır.
supercat,

1
@supercat. . . Dizini, mevcut çift değerler dışında her şeyin üzerine inşa etmek için alternatif bir formülasyon verdim.
Gordon Linoff,

1
İkincisinin çalışması için, birinin listeden bir yinelenen her anahtar değer için bir kimliğe atlandığından emin olması ve listeden kasıtlı olarak çıkarılmış olan öğenin tablodan çıkarılıp çıkarılmadığından emin olması gerekir. eşit anahtarlı bir öğe listeden silinir.
supercat,

@supercat. . . Katılıyorum. Güncelleştirmeler ve silme işlemleri için dizini tutarlı tutmak, diziyi bir tetikleyicide yeniden oluşturamayacağınız için daha zordur. Her durumda, OP'den, verilerin - ya da en azından kopyaların - sık sık değişmediği izlenimini edindim.
Gordon Linoff,

Neden bir kimlik listesi yerine bir değerler listesini dışlamıyorsunuz? Öyleyse, yinelenen değer başına bir kimliği hariç tutulan kimlikler listesinden çıkarmak zorunda değilsiniz
JMD Coalesce

23

Evet bunu yapabilirsin.

İşte yinelenen bir tablo:

CREATE TABLE dbo.Party
  (
    ID INT NOT NULL
           IDENTITY ,
    CONSTRAINT PK_Party PRIMARY KEY ( ID ) ,
    Name VARCHAR(30) NOT NULL
  ) ;
GO

INSERT  INTO dbo.Party
        ( Name )
VALUES  ( 'Frodo Baggins' ),
        ( 'Luke Skywalker' ),
        ( 'Luke Skywalker' ),
        ( 'Harry Potter' ) ;
GO

Mevcut olanları görmezden gelelim ve yeni kopya eklenemediğinden emin olun:

-- Add a new column to mark grandfathered duplicates.
ALTER TABLE dbo.Party ADD IgnoreThisDuplicate INT NULL ;
GO

-- The *first* instance will be left NULL.
-- *Secondary* instances will be set to their ID (a unique value).
UPDATE  dbo.Party
SET     IgnoreThisDuplicate = ID
FROM    dbo.Party AS my
WHERE   EXISTS ( SELECT *
                 FROM   dbo.Party AS other
                 WHERE  other.Name = my.Name
                        AND other.ID < my.ID ) ;
GO

-- This constraint is not strictly necessary.
-- It prevents granting further exemptions beyond the ones we made above.
ALTER TABLE dbo.Party WITH NOCHECK
ADD CONSTRAINT CHK_Party_NoNewExemptions 
CHECK(IgnoreThisDuplicate IS NULL);
GO

SELECT * FROM dbo.Party;
GO

-- **THIS** is our pseudo-unique constraint.
-- It works because the grandfathered duplicates have a unique value (== their ID).
-- Non-grandfathered records just have NULL, which is not unique.
CREATE UNIQUE INDEX UNQ_Party_UniqueNewNames ON dbo.Party(Name, IgnoreThisDuplicate);
GO

Bu çözümü test edelim:

-- cannot add a name that exists
INSERT  INTO dbo.Party
        ( Name )
VALUES  ( 'Frodo Baggins' );

Cannot insert duplicate key row in object 'dbo.Party' with unique index 'UNQ_Party_UniqueNewNames'.

-- cannot add a name that exists and has an ignored duplicate
INSERT  INTO dbo.Party
        ( Name )
VALUES  ( 'Luke Skywalker' );

Cannot insert duplicate key row in object 'dbo.Party' with unique index 'UNQ_Party_UniqueNewNames'.


-- can add a new name 
INSERT  INTO dbo.Party
        ( Name )
VALUES  ( 'Hamlet' );

-- but only once
INSERT  INTO dbo.Party
        ( Name )
VALUES  ( 'Hamlet' );

Cannot insert duplicate key row in object 'dbo.Party' with unique index 'UNQ_Party_UniqueNewNames'.

4
Tabi masaya bir sütun ekleyemiyor.
Aaron Bertrand

3
Bu cevabın NULL değerlerin standart olmayan bir şekilde benzersiz kısıtlamalarla nasıl ele alındığını, yararlı bir şeye dönüştürmesini seviyorum. Kurnaz hile.
ypercubeᵀᴹ

@ ypercubeᵀᴹ, NULL kullanımı ile ilgili olarak benzersiz kısıtlamaların standart olmayan ne olduğunu açıklayabilir misiniz? Beklediğinizden ne kadar farklı? Teşekkürler!
Noach

1
@ SQL Server'da hiçbir, UNIQUEboş bir sütundaki bir kısıtlama, en fazla tek bir NULLdeğer olmasını sağlar . SQL standardı (ve neredeyse diğer tüm SQL DBMS'ler) herhangi bir sayıdaki NULLdeğere izin vermesi gerektiğini söyler (yani kısıtlama boş değerleri yoksaymalıdır).
ypercubeᵀᴹ

@ ypercubeᵀᴹ Bunu farklı bir DBMS'ye uygulamak için, NULL yerine DEFAULT 0 kullanmamız gerekiyor. Doğru?
Noach

16

Filtrelenen benzersiz dizin mükemmel bir fikir ama küçük bir dezavantajı var - WHERE identity_column > <current value>şartı ya da kullanımı farketmez WHERE identity_column NOT IN (<list of ids for duplicate values here>).

İlk yaklaşımla, gelecekteki yinelenen verileri, mevcut (şimdi) verilerin yinelemelerini ekleyebileceksiniz. Örneğin, şu anda (yalnızca bir satır) satırınız varsa CompanyName = 'Software Inc.', dizin aynı şirket adıyla bir satır daha eklenmesini yasaklamaz. Sadece iki kez denerseniz bunu yasaklar.

İkinci yaklaşımla bir gelişme var, yukarıdaki işe yaramayacak (ki bu iyi.) Ancak yine de daha fazla kopya veya mevcut kopya ekleyebileceksiniz. Örneğin, şu anda (iki veya daha fazla) CompanyName = 'DoubleData Co.'satırınız varsa, dizin aynı şirket adıyla bir satır daha eklenmesini yasaklamaz. Sadece iki kez denerseniz bunu yasaklar.

(Güncelleme) Bu, her yinelenen ad için, bir hariç tutma listesinden uzak tutmanız durumunda düzeltilebilir. Yukarıdaki örnekte olduğu gibi, yinelenen CompanyName = DoubleData Co.ve kimlikleri olan 4 satır varsa 4,6,8,9, dışlama listesinde bu kimliklerden yalnızca 3 tanesinin olması gerekir.

İkinci yaklaşımda, başka bir dezavantaj hantal durumdur (ne kadar hantal, ilk etapta kaç kopya olduğuna bağlıdır), çünkü SQL-Server, NOT INoperatörü WHEREfiltrelenmiş indeksler bölümünde desteklemiyor gibi görünmektedir . SQL-Fiddle'a bakınız . Bunun yerine, yüzlerce kopya adınız varsa, böyle bir koşulda verimlilik sonuçları olup olmadığından emin değilim WHERE (CompanyID NOT IN (3,7,4,6,8,9))gibi bir şeye sahip WHERE (CompanyID <> 3 AND CompanyID <> 7 AND CompanyID <> 4 AND CompanyID <> 6 AND CompanyID <> 8 AND CompanyID <> 9)olmalısınız.


Başka bir çözüm (@Alex Kuznetsov'unkilere benzer) başka bir sütun eklemek, onu sayı numaralarıyla doldurmak ve bu sütunu içeren benzersiz bir dizin eklemek:

ALTER TABLE Company
  ADD Rn TINYINT DEFAULT 1;

UPDATE x
SET Rn = Rnk
FROM
  ( SELECT 
      CompanyID,
      Rn,
      Rnk = ROW_NUMBER() OVER (PARTITION BY CompanyName 
                               ORDER BY CompanyID)
    FROM Company 
  ) x ;

CREATE UNIQUE INDEX CompanyName_UQ 
  ON Company (CompanyName, Rn) ; 

Ardından, yinelenen adı olan bir satır eklemek DEFAULT 1özellik ve benzersiz dizin nedeniyle başarısız olur . Bu hala% 100 kusursuz değil (Alex's iken). İfadede Rnaçıkça ayarlanmışsa INSERTveya Rndeğerler kötü amaçlı olarak güncellenirse , yinelenenler yine de kaymaya devam eder .

SQL Fiddle-2


-2

Başka bir alternatif, tabloda zaten bir değer olup olmadığını kontrol eden bir skalar işlevi yazmak ve bu işlevi bir kontrol sınırlamasından çağırmaktır.

Bu performans için korkunç şeyler yapacaktır.



Aaron'un işaret ettiği konuların yanı sıra, cevap, bu kontrol kısıtlamasının nasıl eklenebileceğini açıklamamaktadır, böylece mevcut kopyaları yok sayar.
ypercubeᵀᴹ

-2

Aynı şeyi arıyorum - güvenilir olmayan benzersiz bir dizin oluşturun, böylece mevcut hatalı veriler yoksayılır, ancak yeni kayıtlar zaten var olan hiçbir şeyin kopyası olamaz.

Bu konuyu okurken, daha iyi bir çözümün, üst tabloya karşı çiftleri kontrol edecek [bu] ekleri kontrol edecek bir tetikleyici yazmak olduğu ve bu tabloların arasında herhangi bir kopya varsa, ROLLBACK TRAN geliyor.

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.