Як створити унікальний індекс у стовпці NULL?


101

Я використовую SQL Server 2005. Я хочу обмежити значення в стовпці унікальними, дозволяючи NULLS.

Моє поточне рішення включає в себе унікальний індекс для такого вигляду:

CREATE VIEW vw_unq WITH SCHEMABINDING AS
    SELECT Column1
      FROM MyTable
     WHERE Column1 IS NOT NULL

CREATE UNIQUE CLUSTERED INDEX unq_idx ON vw_unq (Column1)

Якісь кращі ідеї?


16
немає шансів використовувати sql 2008? ви можете створити відфільтрований індекс, використовуючи "де"
Simon_Weaver

3
Ви не мали на увазі унікальні, дозволяючи NULL , ви, мабуть, мали на увазі унікальні, але включаючи декілька NULL . В іншому випадку NULL індексується, як і будь-яке інше значення, і обмеження унікальності працює як очікується - просто не відповідно до стандартів SQL, як @pst, зазначений у коментарі нижче.
Suncat2000

Відповіді:


26

Досить впевнений, що ви не можете цього зробити, оскільки це порушує призначення унікальних предметів.

Однак ця людина, схоже, має гідну роботу: http://sqlservercodebook.blogspot.com/2008/04/multiple-null-values-in-unique-index-in.html


2
Здається, вміст наданого вами посилання насправді (частково) скопійований без атрибуції звідси: decipherinfosys.wordpress.com/2007/11/30/…
Том Юргенс,

77
Я не погоджуюся з тим, що це "порушує призначення унікальних" - NULL - це особливе значення в SQL (подібне багато в чому до NaN) і до нього потрібно ставитися відповідно. Насправді це невдача в SQL Server виконувати різні специфікації SQL: ось посилання на запит на "правильну реалізацію" того, чого воно варто: connect.microsoft.com/SQLServer/feedback/details/299229/… .

5
для довідок у 2008 році ви можете зробити CREATE UNIQUE INDEX foo ON dbo.bar (ключ), де ключ НЕ NULL;
niico

2
Я не погоджуюсь з тим, що "порушує призначення унікальних даних", NULL не дорівнює NULL, тому ви повинні мати можливість створити унікальний індекс на нульовому стовпчику та вставити кілька нулів.
Wodzu

105

Використовуючи SQL Server 2008, можна створити відфільтрований індекс: http://msdn.microsoft.com/en-us/library/cc280372.aspx . (Я бачу, що Саймон додав це як коментар, але подумав, що заслужив власну відповідь, оскільки коментар легко пропускається.)

Інший варіант - це тригер для перевірки унікальності, але це може вплинути на продуктивність.


84
create unique index UIX on MyTable (Column1) where Column1 is not null
Jørn Schou-Rode

1
Примітка: наразі SQL Server Management Studio, здається, не знає, як створити такі індекси, тому, якщо згодом змінити таблицю, вона заплутається і спробує скинути її, тому не забудьте відтворити її
Simon_Weaver

3
Схоже, що Microsoft оновила SSMS, щоб підтримати це. У мене SSMS 10.50.1617, і в діалоговому вікні "Властивості індексу" ви можете вибрати сторінку "Фільтр" для редагування фільтра. напр., "([Колона1] НЕ НУЛЬНА)"
Філ Хасельден

5
Допуск кількох нулів в індексі та фільтрація нулів з індексу - це окремі речі. Фільтрування індексу фактично виключає записи з індексу, тоді як інші рішення перетворюють нуль у корисне унікальне значення. Будьте в курсі різниці.
Suncat2000

Якщо ви використовуєте збережені процедури на таблиці з таким відфільтрованим індексом, переконайтесь, що ANSI_NULLSце є ON, інакше ви отримаєте помилку при спробі вставлення даних.
Арн

71

Розрахунковий трюковий стовпчик широко відомий як "nullbuster"; мої ноти: Стів Касс:

CREATE TABLE dupNulls (
pk int identity(1,1) primary key,
X  int NULL,
nullbuster as (case when X is null then pk else 0 end),
CONSTRAINT dupNulls_uqX UNIQUE (X,nullbuster)
)

Це виглядає як крутий трюк. Як не дивно пошук nullbuster не приносить занадто багато речей. Мені цікаво, чи це буде корисно і для прискорення пошуку - а не для обчислених стовпців усього 1 та 0 для нуля чи ні, якщо використання ПК надає індексу щось більше? Збираємось випробувати ці вихідні на великий стіл і подивимось.
Девід Сторфер

@DavidStorfer, ви не можете цього зробити, оскільки у вас може виникнути зіткнення між ідентифікаторами двох різних таблиць.
користувач393274

Поліпшення: ISNULL (X, CONVERT (VARCHAR (10), pk))
Faiz

5
@Faiz: Поліпшення в очах глядача. Я віддаю перевагу вигляду оригіналу.
день, коли

@NunoG, це має бути прийнятою відповіддю, оскільки воно забезпечує гарне рішення, яке відповідає вашим вимогам, а не просто посилання на зовнішній сайт, який може зникнути.
Фредерік

-3

Строго кажучи, унікальний нульовий стовпець (або набір стовпців) може бути NULL (або записом NULL) лише один раз, оскільки мати одне і те ж значення (а це включає NULL) більше одного разу очевидно порушує унікальне обмеження.

Однак це не означає, що концепція "унікальних нульових стовпців" є дійсною; щоб реально реалізувати його у будь-якій реляційній базі даних, ми просто маємо мати на увазі, що цей тип баз даних має бути нормалізований для належної роботи, а нормалізація зазвичай передбачає додавання декількох (не-сутнісних) додаткових таблиць для встановлення взаємозв'язків між сутностями .

Давайте попрацюємо з базовим прикладом, розглядаючи лише один "унікальний нульовий стовпчик", його легко розгорнути на більше таких стовпців.

Припустимо, ми подаємо інформацію, представлену такою таблицею:

create table the_entity_incorrect
(
  id integer,
  uniqnull integer null, /* we want this to be "unique and nullable" */
  primary key (id)
);

Ми можемо це зробити, розставивши uniqnull і додавши другу таблицю, щоб встановити зв'язок між значеннями uniqnull та the_entity (а не мати uniqnull "всередині the_entity"):

create table the_entity
(
  id integer,
  primary key(id)
);

create table the_relation
(
  the_entity_id integer not null,
  uniqnull integer not null,

  unique(the_entity_id),
  unique(uniqnull),
  /* primary key can be both or either of the_entity_id or uniqnull */
  primary key (the_entity_id, uniqnull), 
  foreign key (the_entity_id) references the_entity(id)
);

Щоб пов’язати значення uniqnull з рядком у the_entity, нам також потрібно додати рядок у the_relation.

Для рядків у the_entity не було пов'язано жодних значень uniqnull (тобто для тих, у яких ми би поставили NULL в the_entity_incorrect), ми просто не додаємо рядок у the_relation.

Зауважте, що значення uniqnull будуть унікальними для всіх відносин__, а також зауважте, що для кожного значення в the_entity може бути не більше одного значення у the_relation, оскільки первинні та зовнішні ключі на ньому примусово застосовують це.

Тоді, якщо значення 5 для uniqnull має бути пов’язане з ідентифікатором__entity 3, нам потрібно:

start transaction;
insert into the_entity (id) values (3); 
insert into the_relation (the_entity_id, uniqnull) values (3, 5);
commit;

І якщо значення id 10 для the_entity не має аналога uniqnull, ми робимо лише:

start transaction;
insert into the_entity (id) values (10); 
commit;

Щоб денормалізувати цю інформацію та отримати дані, якими міститься таблиця на зразок the_entity_incorrect, нам потрібно:

select
  id, uniqnull
from
  the_entity left outer join the_relation
on
  the_entity.id = the_relation.the_entity_id
;

Оператор "лівого зовнішнього з'єднання" гарантує, що всі рядки з the_entity з'являться в результаті, ставлячи NULL у стовпчик uniqnull, коли у the_relation немає відповідних стовпців.

Пам’ятайте, будь-які зусилля, витрачені протягом декількох днів (або тижнів чи місяців) на розробку добре нормалізованої бази даних (і відповідних денормалізуючих поглядів та процедур), заощадять вам роки (або десятиліття) болю та марних ресурсів.


6
Як уже було сказано в коментарі прийнятої відповіді з п'ятдесям оновлень, MS Sql Server має підтримувати її у кількох стовпцях, що індексуються як унікальні. Не допустити впровадження стандартів SQL, щоб цього не допустити. Null не є значенням, null не дорівнює null, тобто основне правило SQL з років. Тож ваше перше речення неправильне, і більшість читачів не будуть турбувати читати далі.
Фредерік
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.