Технічно NULL = NULL є хибним, за цією логікою жоден NULL не дорівнює NULL, і всі NULL є різними. Чи не повинно це означати, що всі NULL є унікальними, а унікальний індекс повинен дозволяти будь-яку кількість NULL?
Технічно NULL = NULL є хибним, за цією логікою жоден NULL не дорівнює NULL, і всі NULL є різними. Чи не повинно це означати, що всі NULL є унікальними, а унікальний індекс повинен дозволяти будь-яку кількість NULL?
Відповіді:
Чому це працює саме так? Тому що назад, коли хтось прийняв дизайнерське рішення, не знаючи і не піклуючись про те, що говорить стандарт (адже ми маємо всілякі дивні форми поведінки з NULL
s і можемо примусити різну поведінку за бажанням). Це рішення продиктувало, що в цьому випадку NULL = NULL
.
Це було не дуже розумне рішення. Що вони повинні були зробити, це те, щоб поведінка за замовчуванням дотримувалася стандарту ANSI, і якщо вони дійсно хотіли такої своєрідної поведінки, дозвольте це за допомогою параметра DDL типу WITH CONSIDER_NULLS_EQUAL
або WITH ALLOW_ONLY_ONE_NULL
.
Звичайно, задній огляд - 20/20.
І зараз у нас є рішення, навіть якщо це не найчистіше чи найінтуїтивніше.
Ви можете отримати належну поведінку ANSI в SQL Server 2008 і вище, створивши унікальний, відфільтрований індекс.
CREATE UNIQUE INDEX foo ON dbo.bar(key) WHERE key IS NOT NULL;
Це дозволяє більше, ніж одне NULL
значення, оскільки ці рядки повністю залишені під час перевірки дублікатів. Як додатковий бонус, це в кінцевому підсумку буде меншим індексом, ніж той, який складався з усієї таблиці, якщо NULL
було дозволено кілька s (особливо коли це не єдиний стовпець в індексі, він містить INCLUDE
стовпці тощо). Однак, можливо, вам потрібно знати про деякі інші обмеження відфільтрованих індексів:
Правильно. Реалізація унікального обмеження або індексу на сервері sql дозволяє отримати один і єдиний NULL. Також правильно, що це технічно не відповідає визначенню NULL, але це одна з тих речей, які вони зробили, щоб зробити його більш корисним, хоча це не "технічно" правильно. Зверніть увагу, що ПЕРШИЙ КЛЮЧ (також унікальний індекс) не дозволяє NULL (звичайно).
По-перше - перестаньте використовувати фразу "Нульове значення", це просто зведе вас з пустощі. Натомість використовуйте словосполучення "нульовий маркер" - маркер у стовпці, який вказує на те, що фактичне значення в цьому стовпці або відсутнє, або непридатне (але зауважте, що маркер не говорить про те, який із цих варіантів насправді є¹).
Тепер уявіть собі наступне (де база даних не має повних знань про модельовану ситуацію).
Situation Database
ID Code ID Code
-- ----- -- -----
1 A 1 A
2 B 2 (null)
3 C 3 C
4 B 4 (null)
Правило цілісності, яке ми моделюємо, - «Кодекс повинен бути унікальним». Реальна ситуація порушує це, тому база даних не повинна дозволяти обом пунктам 2 і 4 одночасно знаходитись у таблиці.
Найбезпечнішим і найменш гнучким підходом було б забороняти нульові маркери в полі Код, тому немає можливості суперечливих даних. Найбільш гнучким підходом було б дозволити кілька нульових маркерів і турбуватися про унікальність при введенні значень.
Програмісти Sybase пішли з дещо безпечним, не дуже гнучким підходом, дозволяючи лише один нульовий маркер у таблиці - щось з того часу скаржилися коментатори. Microsoft продовжував цю поведінку, я думаю, що для зворотної сумісності.
¹ Я впевнений, що десь читав, що Кодд розглядав можливість впровадження двох нульових маркерів - одного для невідомого, одного для непридатного - але відхилив його, але не можу знайти посилання. Я правильно пам’ятаю?
PS Моя улюблена цитата про null: Луї Девідсон, "Професійний дизайн баз даних SQL Server 2000", Wrox Press, 2001, стор. 52. "Зведено до одного речення: NULL - це зло".
null
не досягає і цієї мети. Тому що відсутнє значення може виявитись таким же, як значення в одному з інших рядків.
CHECK (Value IN ('A','B','C','D'))
? Тоді як реалізація SQL-сервера, так і стандарт SQL дозволяють таблиці мати 5 рядків (один рядок для кожного значення плюс 1 з NULL.) Тоді, мабуть, хоча база даних узгоджується зі своїми обмеженнями, вона не відповідає намірам дизайнера для таблиця повинна мати максимум 4 ряди. Немає значення, що NULL може бути змінено на таке, що не порушить обмеження, якщо не буде видалено один або кілька рядків.
CREATE TABLE #T(A INT NULL UNIQUE);INSERT INTO #T VALUES (1),(NULL);UPDATE #T SET A = 1 WHERE A IS NULL;
призведе до помилки. Згідно з вашою теорією дизайнерських мотивацій, NULL
у першому випадку слід було б перешкодити введенню - адже неповні знання означають, що немає гарантії того, що значення є іншим.
Це може бути не технічно точно, але по-філософськи це допомагає мені спати вночі ...
Як і декілька інших, на які говорили або на які згадувалося, якщо ви вважаєте, що NULL є невідомим, то ви не можете визначити, чи є одне значення NULL насправді іншим NULL. Думаючи про це таким чином, вираз NULL == NULL слід оцінювати до NULL, тобто невідомо.
Унікальне обмеження потребує остаточного значення для порівняння значень стовпців. Іншими словами, при порівнянні одного значення стовпця з будь-яким іншим значенням стовпця, використовуючи оператор рівності, він повинен оцінювати, що значення false відповідає дійсності. Невідомий насправді неправдивий, хоча до нього часто ставляться як до хитрості. Два значення NULL можуть бути рівними, чи ні ... їх просто неможливо визначити остаточно.
Це допомагає мислити унікальне обмеження як обмежуючі значення, які можна визначити як відмінні одне від одного. Що я маю на увазі під цим, якщо ви запустите SELECT, який виглядає приблизно так:
SELECT * from dbo.table1 WHERE ColumnWithUniqueContraint="some value"
Більшість людей очікували б одного результату, враховуючи, що існує унікальне обмеження. Якщо ви дозволили декілька значень NULL у ColumnWithUniqueConstraint, то неможливо було б вибрати один окремий рядок із таблиці, використовуючи NULL як порівняне значення.
Враховуючи це, я вважаю, що незалежно від того, точно реалізовано чи ні щодо визначення NULL, це, безумовно, набагато практичніше в більшості ситуацій, ніж допускати кілька значень NULL.
Однією з головних цілей UNIQUE
обмеження є запобігання повторюваних записів. Якщо потрібно мати таблицю, в якій може бути кілька записів, де значення "невідомо", але жодним записам не дозволяється мати однакове "відоме" значення, то невідомим значенням слід присвоїти штучні унікальні ідентифікатори, перш ніж вони будуть додано до таблиці.
Є кілька рідкісних випадків, коли стовпець, який має UNIQUE
обмеження і містить єдине нульове значення; Наприклад, якщо таблиця містить відображення між значеннями стовпців та локалізованими текстовими описами, рядок для NULL
дасть змогу визначити опис, який повинен з’являтися, коли цей стовпець у якійсь іншій таблиці є NULL
. Поведінка NULL
дозволу для цього випадку використання.
В іншому випадку я не бачу підстав для бази даних із UNIQUE
обмеженням для будь-якого стовпця, що дозволяло б існувати безліч ідентичних записів, але я не бачу способу запобігти цьому, дозволяючи кілька записів, ключові значення яких не можна відрізняти. Заявивши, що NULL
не є рівними собі, NULL
значення не відрізнятимуться один від одного.