O índice exclusivo filtrado é uma idéia brilhante, mas possui uma pequena desvantagem - não importa se você usa a WHERE identity_column > <current value>
condição ou o WHERE identity_column NOT IN (<list of ids for duplicate values here>)
.
Com a primeira abordagem, você ainda poderá inserir dados duplicados no futuro, duplicados dos dados existentes (agora). Por exemplo, se você tiver (mesmo apenas uma) linha agora CompanyName = 'Software Inc.'
, o índice não proibirá a inserção de mais uma linha com o mesmo nome da empresa. Só o proibirá se você tentar duas vezes.
Com a segunda abordagem, há uma melhoria, o acima não funcionará (o que é bom.) No entanto, você ainda poderá inserir mais duplicatas ou duplicatas existentes. Por exemplo, se você tiver (duas ou mais) linhas agora com CompanyName = 'DoubleData Co.'
, o índice não proibirá a inserção de mais uma linha com o mesmo nome da empresa. Só o proibirá se você tentar duas vezes.
(Atualização) Isso pode ser corrigido se, para cada nome duplicado, você mantiver fora da lista de exclusões um ID. Se, como no exemplo acima, houver 4 linhas com CompanyName = DoubleData Co.
IDs e duplicados 4,6,8,9
, a lista de exclusão deverá ter apenas 3 desses IDs.
Com a segunda abordagem, outra desvantagem é a condição complicada (quanto pesada depende de quantas duplicatas existem), pois o SQL-Server parece não oferecer suporte ao NOT IN
operador na WHERE
parte dos índices filtrados. Veja SQL-Fiddle . Em vez disso WHERE (CompanyID NOT IN (3,7,4,6,8,9))
, você terá que ter algo como WHERE (CompanyID <> 3 AND CompanyID <> 7 AND CompanyID <> 4 AND CompanyID <> 6 AND CompanyID <> 8 AND CompanyID <> 9)
não tenho certeza se há implicações de eficiência com essa condição, se você tiver centenas de nomes duplicados.
Outra solução (semelhante à do @Alex Kuznetsov) é adicionar outra coluna, preenchê-la com números de classificação e adicionar um índice exclusivo, incluindo esta coluna:
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) ;
Em seguida, a inserção de uma linha com nome duplicado falhará devido à DEFAULT 1
propriedade e ao índice exclusivo. Isso ainda não é 100% infalível (enquanto o de Alex é). As duplicatas ainda serão Rn
inseridas se estiver explicitamente definido na INSERT
instrução ou se os Rn
valores forem atualizados com códigos maliciosos.
SQL-Fiddle-2