Отфильтрованный уникальный индекс - блестящая идея, но он имеет небольшой недостаток - независимо от того, используете ли вы WHERE identity_column > <current value>
условие или WHERE identity_column NOT IN (<list of ids for duplicate values here>)
.
При первом подходе вы все равно сможете вставлять дубликаты данных в будущем, дубликаты существующих (сейчас) данных. Например, если у вас есть (даже только одна) строка с CompanyName = 'Software Inc.'
индексом, индекс не будет запрещать вставку еще одной строки с таким же названием компании. Это только запретит, если вы попробуете дважды.
Со вторым подходом есть улучшение, вышеупомянутое не будет работать (что хорошо.) Однако вы все равно сможете вставить больше дубликатов или существующих дубликатов. Например, если у вас есть (две или более) строки теперь с CompanyName = 'DoubleData Co.'
индексом, индекс не будет запрещать вставку еще одной строки с таким же названием компании. Это только запретит, если вы попробуете дважды.
(Обновление) Это можно исправить, если для каждого дублированного имени вы исключаете из списка исключений один идентификатор. Если, как и в приведенном выше примере, имеется 4 строки с дубликатами CompanyName = DoubleData Co.
и идентификаторами 4,6,8,9
, в списке исключений должно быть только 3 из этих идентификаторов.
При втором подходе еще одним недостатком является громоздкое условие (насколько громоздким оно зависит от того, сколько дубликатов существует в первую очередь), поскольку SQL-Server, похоже, не поддерживает NOT IN
оператор в WHERE
части отфильтрованных индексов. Смотрите SQL-Fiddle . Вместо этого у WHERE (CompanyID NOT IN (3,7,4,6,8,9))
вас должно быть что-то вроде того, WHERE (CompanyID <> 3 AND CompanyID <> 7 AND CompanyID <> 4 AND CompanyID <> 6 AND CompanyID <> 8 AND CompanyID <> 9)
что я не уверен, есть ли последствия для эффективности с таким условием, если у вас есть сотни повторяющихся имен.
Другое решение (похожее на @Alex Kuznetsov's) заключается в добавлении еще одного столбца, заполнении его номерами рангов и добавлении уникального индекса, включающего этот столбец:
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) ;
Затем вставка строки с повторяющимся именем завершится неудачно из-за DEFAULT 1
свойства и уникального индекса. Это все еще не на 100% надежно (в то время как у Алекса). Дубликаты по-прежнему будут добавляться, если Rn
в INSERT
операторе явно указано значение или Rn
значения были злонамеренно обновлены.
SQL-Fiddle-2