Filtrowany unikalny indeks jest genialnym pomysłem, ale ma niewielką wadę - bez względu na to, czy używasz WHERE identity_column > <current value>
warunku, czy WHERE identity_column NOT IN (<list of ids for duplicate values here>)
.
Przy pierwszym podejściu nadal będziesz mógł wstawiać zduplikowane dane w przyszłości, duplikaty istniejących (teraz) danych. Na przykład, jeśli masz teraz (nawet tylko jeden) wiersz CompanyName = 'Software Inc.'
, indeks nie zabrania wstawiania jeszcze jednego wiersza o tej samej nazwie firmy. Zabrania to tylko, jeśli spróbujesz dwa razy.
W drugim podejściu jest poprawa, powyższe nie zadziała (co jest dobre.) Jednak nadal będziesz mógł wstawić więcej duplikatów lub istniejących duplikatów. Na przykład, jeśli masz teraz (dwa lub więcej) wierszy CompanyName = 'DoubleData Co.'
, indeks nie zabrania wstawiania jeszcze jednego wiersza o tej samej nazwie firmy. Zabrania to tylko, jeśli spróbujesz dwa razy.
(Aktualizacja) Można to poprawić, jeśli dla każdej zduplikowanej nazwy trzymasz z listy wykluczeń jeden identyfikator. Jeśli, podobnie jak w powyższym przykładzie, są 4 wiersze z duplikatem CompanyName = DoubleData Co.
i identyfikatorami 4,6,8,9
, lista wykluczeń powinna mieć tylko 3 z tych identyfikatorów.
Przy drugim podejściu kolejną wadą jest kłopotliwy warunek (ile kłopotliwy zależy od liczby duplikatów w pierwszej kolejności), ponieważ SQL Server nie wydaje się obsługiwać NOT IN
operatora w WHERE
części przefiltrowanych indeksów. Zobacz SQL-Fiddle . Zamiast tego WHERE (CompanyID NOT IN (3,7,4,6,8,9))
musisz mieć coś takiego WHERE (CompanyID <> 3 AND CompanyID <> 7 AND CompanyID <> 4 AND CompanyID <> 6 AND CompanyID <> 8 AND CompanyID <> 9)
, że nie jestem pewien, czy z takim stanem mają wpływ na wydajność, jeśli masz setki zduplikowanych nazw.
Innym rozwiązaniem (podobnym do @Aleksa Kuzniecowa) jest dodanie kolejnej kolumny, wypełnienie jej numerami rang i dodanie unikalnego indeksu obejmującego tę kolumnę:
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) ;
Wstawienie wiersza o zduplikowanej nazwie nie powiedzie się z powodu DEFAULT 1
właściwości i unikalnego indeksu. To wciąż nie jest w 100% niezawodne (podczas gdy Alex jest). Duplikaty nadal będą się pojawiać, jeśli Rn
jest to jawnie ustawione w INSERT
instrukcji lub jeśli Rn
wartości są złośliwie aktualizowane.
SQL-Fiddle-2