Фільтрований унікальний індекс - це геніальна ідея, але він має незначний недолік - незалежно від того, використовуєте ви 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) - додати ще один стовпець, заповнити його ранговими номерами та додати унікальний індекс, що включає цей стовпець:
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