SQL Server 2008 і вище
Просто відфільтруйте унікальний індекс:
CREATE UNIQUE NONCLUSTERED INDEX UQ_Party_SamAccountName
ON dbo.Party(SamAccountName)
WHERE SamAccountName IS NOT NULL;
У нижчих версіях матеріалізований вигляд все ще не потрібен
Для SQL Server 2005 та новіших версій ви можете це робити без перегляду. Я щойно додав унікальне обмеження, як ви просите, до однієї з моїх таблиць. Враховуючи, що я хочу унікальності в стовпці SamAccountName
, але я хочу дозволити кілька NULL, я використовував матеріалізований стовпець, а не матеріалізований вигляд:
ALTER TABLE dbo.Party ADD SamAccountNameUnique
AS (Coalesce(SamAccountName, Convert(varchar(11), PartyID)))
ALTER TABLE dbo.Party ADD CONSTRAINT UQ_Party_SamAccountName
UNIQUE (SamAccountNameUnique)
Вам просто потрібно помістити в обчислений стовпець щось, що буде гарантовано унікальним у всій таблиці, коли фактичний потрібний унікальний стовпець буде NULL. У цьому випадку PartyID
стовпчик ідентичності є чисельним числом і ніколи не відповідає жодному SamAccountName
, тому він працював для мене. Ви можете спробувати власний метод - будьте впевнені, що ви розумієте домен своїх даних, щоб не було можливості перетину реальних даних. Це може бути таким же простим, як попередження подібного символу диференціатора:
Coalesce('n' + SamAccountName, 'p' + Convert(varchar(11), PartyID))
Навіть якщо PartyID
колись вона стала нечисловою і могла збігатися з a SamAccountName
, тепер це не має значення.
Зауважте, що наявність індексу, що включає обчислений стовпець, неявно призводить до збереження кожного результату вираження на диску з іншими даними таблиці, які НЕ займають додаткового місця на диску.
Зауважте, що якщо ви не хочете індексу, ви все одно можете зберегти ЦП, зробивши вираз попередньо обчислений на диску, додавши ключове слово PERSISTED
до кінця визначення виразу стовпця.
У SQL Server 2008 і новіших версіях обов'язково використовуйте відфільтрований розчин замість цього, якщо можливо.
Суперечки
Зверніть увагу, що деякі фахівці з баз даних сприйматимуть це як випадок "сурогатних NULL", які, безумовно, мають проблеми (в основному через проблеми навколо спроби визначити, коли щось є реальним значенням або сурогатним значенням для відсутніх даних ; також можуть виникнути проблеми з числом сурогатних значень, що не мають NULL, множиться як божевільні).
Однак я вважаю, що ця справа інша. Обчислюваний стовпець, який я додаю, ніколи не буде використовуватися для визначення нічого. Він не має сенсу сам по собі і не кодує ніякої інформації, яка вже не зустрічається окремо в інших, правильно визначених стовпцях. Його ніколи не слід вибирати чи використовувати.
Отже, моя історія полягає в тому, що це не сурогатний NULL, і я дотримуюся цього! Оскільки ми насправді не хочемо значення NULL з будь-якою метою, крім того, щоб обдурити UNIQUE
індекс, щоб ігнорувати NULL, у нашому випадку використання немає жодної проблеми, що виникає при звичайному сурогатному створенні NULL.
Все, що було сказано, я не маю жодних проблем із використанням індексованого виду, але це викликає деякі проблеми, такі як вимога використання SCHEMABINDING
. Приємно додайте новий стовпець до базової таблиці (вам потрібно як мінімум скинути індекс, а потім випустити подання або змінити вигляд, щоб він не був прив’язаний до схеми). Дивіться повний (довгий) список вимог до створення індексованого виду в SQL Server (2005) (також пізніші версії), (2000) .
Оновлення
Якщо ваш стовпець чисельний, може виникнути завдання забезпечити, щоб використання унікального обмеження Coalesce
не призвело до зіткнень. У цьому випадку є деякі варіанти. Можливо, використовувати негативне число, ставити "сурогатні NULL" лише в негативний діапазон, а "реальні значення" лише в позитивному діапазоні. Як варіант, може бути використаний наступний зразок. У таблиці Issue
(де IssueID
знаходиться PRIMARY KEY
) може бути або не бути TicketID
, але якщо така є, вона повинна бути унікальною.
ALTER TABLE dbo.Issue ADD TicketUnique
AS (CASE WHEN TicketID IS NULL THEN IssueID END);
ALTER TABLE dbo.Issue ADD CONSTRAINT UQ_Issue_Ticket_AllowNull
UNIQUE (TicketID, TicketUnique);
Якщо у IssueID 1 є квиток 123, UNIQUE
обмеження буде на значеннях (123, NULL). Якщо у IssueID 2 немає квитка, він увімкнеться (NULL, 2). Деякі думки покажуть, що це обмеження не може бути дубльоване для жодного рядка таблиці, і все ж дозволяє кілька NULL.