Недоліки використання нульового зовнішнього ключа замість створення таблиці перетину


15

Скажіть, у мене є така діаграма ER:

введіть тут опис зображення

Тепер, якщо я представляв взаємозв'язок за допомогою зовнішнього ключа Schoolв Student, я міг би мати NULLзначення (тому що a Student не потрібно належати до a School), наприклад:

введіть тут опис зображення

Таким чином, правильний спосіб (заснований на прочитаному) - створити таблицю перетину для представлення відносин, наприклад:

введіть тут опис зображення

Таким чином, ніякі NULLзначення не можуть бути присутніми в таблиці School_has_Student.

Але які недоліки використовувати нульовий зовнішній ключ замість створення таблиці перетину?


Редагувати:

Я помилково вибрав ( school_id, student_id) основним ключем School_has_Studentтаблиці, що зробило відносини "багатьма". Правильним первинним ключем повинен був бути student_id:

введіть тут опис зображення


7
Немає «правильного» способу. Існує лише той спосіб, який найкраще підходить для ваших потреб.
MetaFight

1
Я погоджуюсь з Доком щодо помилкової передумови, але, можливо, все ще достатньо зрозуміло, щоб відповісти?
MetaFight

Існує помилкова передумова, але досить легко випрямити і пояснити різницю.

Я відмовився від свого близького голосування, але речення "Отже, правильний спосіб (на основі того, що я прочитав) - створити таблицю перетину, яка б представляла стосунки", створює враження, що ви повинні сказати нам, яке джерело відхилення сказало вам, що це " правильний "спосіб. У кожній текстовій книзі, яку я читав раніше, канонічний шлях для відносин 1: n - це єдиний іноземний ключ. Або ти щось неправильно зрозумів?
Док Браун

@Doc Brown Я не пам'ятаю, де я це прочитав, але впевнений, що в ньому сказано, що таблиця перетину була правильним шляхом. У будь-якому випадку, чи можете ви дати мені назву книги, яка говорить про те, що відносини 1: n (з необов'язковою участю на стороні: 1) повинні бути представлені за допомогою одного іноземного ключа, мені цікаво прочитати, що вони говорять про цю тему.
Том

Відповіді:


18

Дві моделі представляють різні відносини.

Використовуючи таблицю приєднання, ви моделюєте відносини «багато-багато».

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

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


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

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

Якщо ви хочете відносини "один на багато", вам набагато краще використовувати єдину таблицю і дозволяти ідентифікатору школи бути нульовим у таблиці учнів. Немає підстав уникати нулів у полях навіть для зовнішнього ключа. Це означає, що зовнішні стосунки необов’язкові: розробники та DBA розуміють це чітко, і базовий двигун бази даних, безумовно, повинен працювати добре.

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


Отже, якщо я моделюю взаємовідносини "багато хто" (з необов'язковою участю на стороні: 1), я повинен використовувати зовнішній ключ, незважаючи на те, що він може мати NULLзначення?
Том

1
@Так, саме так можна моделювати. Хоча технічно можливо використовувати таблицю приєднання, модель даних дозволяє від багатьох до багатьох, тому для запобігання цього вам потрібні тригери та логіка бази даних. Вам краще, обмеживши стосунки таким чином, що неможливо додати невірні дані.

1
Я редагував своє запитання. Я зробив student_idлише первинний ключ у School_has_Studentтаблиці, який підтримував відносини як один-до-багатьох. Які недоліки має цей метод у використанні іноземного ключа?
Том

@Тому я відредагував свою відповідь.

6

Ви написали в коментарі вище:

книга "Основи систем баз даних" [...] говорить [...], що рекомендується використовувати таблицю перетину, якщо в стовпці із зовнішнім ключем багато значень NULL (наприклад: якщо 98% працівників не керуйте відділом)

Коли в стовпці із зовнішнім ключем багато значень NULL, вашим програмам доведеться мати справу з цим здебільшого порожнім стовпцем для кожного запису, який вони обробляють. Стовпець, ймовірно, буде займати деяке місце на диску, навіть якщо в 98% всіх випадків його порожнє, запит на взаємозв'язок означає запит на той стовпець, який дає вам більше мережевого трафіку, і якщо ви використовуєте ORM, який генерує вам класи з ваших таблиць, ваших програм також буде потрібно більше місця на стороні клієнта, ніж потрібно. Використання таблиці перетину дозволяє уникнути цього, потрібні будуть лише записи посилань, коли еквівалентний зовнішній ключ не був би NULL інакше.

На противагу цьому, якщо у вас є не декілька значень NULL, скажімо, що 50% або більше відносин не є NULL, використання таблиці перетину дає протилежний ефект - більше дискового простору, більша складність, що призводить до збільшення мережевого трафіку тощо.

Тож використання таблиці перетину - це лише форма оптимізації, яка є розумною лише для конкретного випадку, і особливо сьогодні, коли дисковий простір та пам'ять стали дешевшими, набагато рідше потрібними. Зауважте, що "Основи систем баз даних" спочатку писалися більше 20 років тому (я знайшов посилання на друге видання з 1994 р.), І я здогадуюсь, що рекомендація вже була там. До 1994 року оптимізація простору була, ймовірно, набагато важливішою, ніж сьогодні, оскільки масове зберігання все ще було дорожчим, а комп’ютери та мережі були набагато повільнішими, ніж сьогодні.

Як додаткове зауваження до прискіпливого коментаря: вищезазначене твердження просто намагається передбачити, що автор «Основ систем баз даних» мав на увазі у своїй рекомендації, я думаю, що він робив грубе загальне твердження, справедливе для більшості систем. У деяких базах даних є й інші можливі оптимізації, такі як "розріджені стовпці", які роблять використання таблиці перетину ще більш застарілим.

Тому не сприймайте цю рекомендацію неправильно. Книга не говорить про те, щоб віддавати перевагу перехрестям таблиць для {0,1}:nвзаємин взагалі, або - як ви писали - що це "правильний шлях". Використовуйте такі оптимізації, які ускладнять ваші програми лише тоді, коли вони вам справді потрібні.


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

@gardenhead: що змушує вас повірити, що це "більш ніж ймовірно"?
Док Браун

Справа в тому, що бази даних існують десятиліттями і є оптимізованими, оскільки вони є критичним компонентом більшості інфраструктур.
садок

@gardenhead: мені здається, ти робиш набагато більше невиправданих припущень, ніж я. Тим не менш, дивіться мою редакцію.
Док Браун

2

Концептуальна модель виглядатиме так, що є неортодоксальним, якщо сказати менше:

введіть тут опис зображення

Фізична модель буде виглядати приблизно так, що менш заплутано (люди думають, що це M: M, якщо вони не побачать уважно):

введіть тут опис зображення

Моя пропозиція:

Якщо вам подобається, багато стовпців (ФК чи іншим чином), які не стосуються більшості учнів, розділіть таблиці на рольові таблиці по 1: 1 реляції. Але це не тому, що вони є FK, це тому, що стовпці не застосовуються до більшості рядків.

В іншому випадку , незмінні FK - це нормальна частина бази даних, а таблиці приєднання зазвичай для M: M rels.

Загальне використання релей 1: 1 призначене для таблиць ролей, що містять стовпці, які застосовуються лише в тому випадку, якщо сутність певного типу, та витягування стовпців BLOB для міркувань продуктивності чи зберігання. Виведення нульових значень у FKs - це не одне поширене використання для цього.

введіть тут опис зображення


2

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

1) Школа учня (якщо така є) невідома (це стандартне значення 'null' - значення невідомо)

2) Відомо, чи є у школяра школа чи ні, а їх немає

Якщо ви використовуєте стандартне значення null, як би ви представляли "студент не має школи" у вашій моделі іноземного ключа. У цьому випадку вам, мабуть, доведеться створити запис "немає школи", з його власним ідентифікатором у шкільній таблиці. (Не ідеально)


2
У книзі "Основи систем баз даних" зазначається, що існує 3 інтерпретації NULL, це може означати: 1) Невідоме значення. 2) Недоступне або утримане значення. 3) Непридатний атрибут (я думаю, що це тлумачення означає, що ви можете вказати а NULLдля зовнішнього ключа).
Том

1
Це корисний список, але семантика нуля (або будь-яке значення дійсно) визначається користувачем. Тобто це може означати все, що дизайнер каже, що це означає, не обмежуючись цим списком. Питання полягає в тому, як розрізняти різні значення, коли може знадобитися більше (або навіть ненавмисно зберегти)
Бред Томас

Тож ви пропонуєте мені створити таблицю перехрестя замість використання нульового зовнішнього ключа?
Том

@Так, я вважаю, що в цьому випадку краще
Бред Томас

@BradThomas - щоб уникнути такої ж неоднозначності при використанні таблиці перетину, ви представляєте випадок 2 (відомо, що у школяра немає школи) записом у таблиці перетину з NULL School_ID?
andrew

1

У таблицях баз даних є ця приємна річ, яка називається обмеженнями. Тому дуже просто зробити таблицю перехрестя, яка дозволяє лише 1 кожному студенту з'являтися в таблиці, але багато шкіл цієї таблиці. Ефективно дає вам

Теорія приємна, але врешті-решт ви збираєтеся моделювати свою базу даних після запитань.

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

У базах даних: оптимізуйте для запитань.


0

Є випадок використання, коли використання третьої таблиці насправді може мати сенс. Приклад може здатися чисто гіпотетичним, але я сподіваюся, що це добре ілюструє мою думку. Припустимо, ви додасте більше стовпців до studentsтаблиці, і в якийсь момент ви вирішите надати унікальність записів за допомогою складеного індексу в декількох стовпцях. Дуже ймовірно, що вам доведеться включитиschool_id стовпчик, і тут все починає псуватися. Завдяки тому, як був спроектований SQL, можна буде вставити кілька однакових записів, де school_idце NULLможливо. Це має ідеальний сенс з технічної точки зору, але є протиінтуїтивним і може призвести до несподіваних результатів. З іншого боку, домогтися унікальності на таблиці перетину легко.

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

Отже, як ви бачите, це досить конкретний випадок, і, як зазначали інші, у більшості випадків вам було б абсолютно нормально з усіма NULLзначеннями. Це дійсно залежить від конкретних вимог вашої моделі.


0

Окрім безлічі вже поданих хороших пропозицій, особисто я не прихильник іноземних ключів, якщо вони справді не потрібні. Спочатку є відносини M: M, на які ви посилаєтесь. Крім того, виклик іноземного ключа і тим самим втягнення даних таблиці у ваші запити вводить більшу складність і залежно від розміру таблиці, повільнішу ефективність. Як говорили інші, зведені поля FK можуть бути непідтримувані і можуть створювати проблеми з цілісністю даних.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.