Віддаєте перевагу нормалізації бази даних проти прозорості схеми?


10

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

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

Для уточнення абстрагування розгляньте три групи розрізнених даних користувачів: професори, адміністрація, студенти (ні, це не домашнє завдання. Обіцяйте!)

Картографування 1

(Professor_id, admin_id та student_id - це іноземні ключі до відповідних таблиць)

| mailing_id (KEY) | professor_id | admin_id | student_id | 
-------------------------------------------------------
| 1001             |     NULL     |    87    |  NULL      |
| 1002             |     123      |   NULL   |  NULL      |
| 1003             |     NULL     |   NULL   |  123       |

Мінуси +/- для цього підходу здаються досить важкими:

  • Два поля "даремно" в ряд
  • Порушує 2NF
  • Вразлива для вставки / оновлення аномалій (рядок із лише 0-1 набором поля NULL, наприклад)

Плюси не без власних достоїнств, хоча:

  • Відображення може бути здійснено за допомогою одного пошуку
  • Легко визначте "вихідні" дані для певного користувача з mailing_id

Правду кажучи, в моєму кишечнику мені ця ідея зовсім не подобається.

Картографування 2

(припустимо, що MSG_ * є визначеними константами, типами перерахунків або іншим відповідним ідентифікатором)

| mailing_id (KEY)  | user_type (UNIQUE1) | internal_id (UNIQUE2)| 
------------------------------------------------------------------
| 1001              | MSG_ADMIN          | 87                    |
| 1002              | MSG_PROF           | 123                   |
| 1003              | MSG_STUDENT        | 123                   |

З цією установкою та унікальним складеним індексом {user_type, Internal_id} речі стають набагато чистішими, зберігається 3NF, і код програми не повинен перевіряти аномалії вводу / виводу.

З іншого боку, є деяка втрата прозорості у визначенні таблиць джерел користувача, які повинні оброблятися поза БД, в основному це дорівнює прикладному рівню відображення значень типу user_type у таблиці. Зараз я (досить сильно) схиляюся до цього 2-го картографування, оскільки зворотний бік досить незначний.

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


2
Ви можете знайти ідеї Мартіна Фаулера щодо ролей цікавим для читання.
Мар'ян Венема

Це було, справді, цікаво. На жаль, не надто багато розуміння моєї конкретної проблеми
GeminiDomino

Ви збираєтесь отримувати професорів, які стають адміністраторами та студенти, які отримують роботу в адміністрації або навіть повертаються через 10 років на факультет. Ви, мабуть, вже їх маєте. Чи збираєтесь ви тримати їх окремо або намагаєтесь уніфікувати?
Елін

Ролі - це лише приклади, але я бачу ваш пункт. На практиці, навіть якби користувачі міняли ролі, вони все одно залишатимуться як окремі записи.
GeminiDomino

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

Відповіді:


1

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

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

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

"Проблемна дитина", яка доставляє дискомфорт у " Mapping 2", - це USER_TYPEстовпець. Цей стовпець важливий, тому що він потрібен для того, щоб він INTERNAL_IDвідображався не більше одного разу за типом користувача. Єдиний раз, коли вам потрібен будь-який код, про який навіть відомо, USER_TYPEце код, який вставляє та видаляє зі своєї таблиці зіставлення. Це можна досить добре локалізувати. Я припускаю, що ви створите єдину точку у своєму коді, де зберігається вміст таблиці відображення. Додатковий стовпець у цьому місці, де записуються дані , не є великою справою. Те, що ви насправді хочете уникати, - це додавання додаткового стовпця скрізь, коли дані читаються .

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


3

З досвіду, моя рекомендація - це вибирати послідовність щодо елегантності чи «найкращої практики». Тобто відповідати існуючому дизайну та мати три ТРИ таблиці розсилки (по одній для кожної ролі) з простою mailing_id, user_idструктурою поля.

Це не елегантно, але має ряд переваг ...

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

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


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

Я бачу, на що ви потрапляєте, але працюючи над такою системою, я дав варіант, який ви, можливо, не розглядали. Наскільки я бачу, для ідентифікатора розсилки потрібен якийсь вміст, де б він десь посилався (що було надіслано поштою чи як знайти документ), тож ідентифікатор розсилки повинен бути зовнішнім ключем у будь-якому разі, що означає, що питання унікальності будуть вирішені в іншому місці. Коли я читаю, таблиці даних студента-адміністратора та професора, пов’язані з ними, можуть мати різні структури, тому я не бачу додавання значення поля користувача. Оригінальні розробники, мабуть, торкнулися цього питання, що вони зробили?
Джеймс Снелл

Поле "тип користувача" визначає, яку таблицю потрібно асоціювати з певним записом. Їх потрібно було б обробляти на рівні програми в будь-якому випадку, і оскільки вони знаходяться в різних таблицях, немає жодного хорошого способу зробити це обмеженням зовнішнього ключа. На жаль, оригінальні розробники, на жаль, взагалі не розглядали цю проблему, і саме тому вона перетворюється на такий безлад. :)
GeminiDomino
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.