Предпочитаете нормализацию базы данных по сравнению с прозрачностью схемы?

10

В старой кодовой базе появилось новое требование, которое в основном обеспечивает прямую (внутреннюю) связь между двумя ранее не связанными напрямую классами пользователей (хранящимися в разных таблицах с совершенно другой схемой, и, к сожалению, код практически не поддерживает ОО, очень менее разработанный, так что нет родительского класса). Поскольку мы стремимся повесить сумку на эту старую установку, которая никогда не учитывала эту функциональность, нет гарантии, что не будет коллизий PK - учитывая используемый набор данных, практически гарантировано, что есть.

Таким образом, решение кажется очевидным: убить его с помощью огня и переписать всю таблицу ошибок. Я получил два направления для возможных способов реализации карты, но я не администратор баз данных, поэтому я не уверен, есть ли какие-то плюсы и минусы, которые я пропустил.

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

Картирование 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       |

+/- к этому подходу кажется довольно тяжелым минусы:

  • Два «впустую» поля в строке
  • Нарушает 2НФ
  • Уязвим для вставки / обновления аномалий (например, строка с полем 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-му отображению, так как недостаток довольно незначительный.

НО я до боли осознаю свои собственные ограничения, и я уверен, что я, вероятно, упустил преимущества или камни преткновения в обоих направлениях, поэтому я обращаюсь к более разумным умам, чем мой.

GeminiDomino
источник
2
Вы можете найти идеи Мартина Фаулера о ролях интересным чтением.
Марьян Венема
Это было действительно интересно. К сожалению, не слишком много понимания моей конкретной проблемы
GeminiDomino
Вы получите профессоров, которые станут администраторами и студентами, которые получат работу в администрации или даже 10 лет спустя вернутся на факультеты. Вы, наверное, уже есть. Собираетесь ли вы оставить это отдельно или попытаться объединиться?
Элин
Роли всего лишь примеры, но я понимаю вашу точку зрения. На практике, даже если пользователи поменялись ролями, они все равно остались бы отдельными записями.
Близнецы Домино
Если было бы здорово, если бы вы перефразировали первый абзац. Это немного неясно. Я имею в виду, очевидно, что есть проблема, но недостаточно ясно, что это такое.
Тулаинс Кордова

Ответы:

1

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

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

У вас уже есть больше кода, чем вы хотели бы иметь, поэтому не добавляйте больше кода, чем это абсолютно необходимо для поддержания согласованности вашего интегрированного списка ключей. Позвольте вашей базе данных делать то, для чего она была создана.

«Проблемный ребенок», вызывающий дискомфорт в Mapping 2, - это USER_TYPEстолбец. Этот столбец важен, поскольку он необходим для того, чтобы он INTERNAL_IDотображался не более одного раза для каждого типа пользователя. Единственный раз, когда вам нужен какой-либо код, о котором даже известно, USER_TYPEэто код, который вставляется и удаляется из вашей таблицы сопоставления. Это может быть локализовано довольно хорошо. Я бы предположил, что вы создадите единственную точку в вашем коде, где поддерживается содержимое таблицы сопоставления. Дополнительный столбец в этом месте, где записываются данные , не имеет большого значения. Чего вы действительно хотите избежать, так это добавлять дополнительный столбец везде, где читаются данные .

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

Джоэл Браун
источник
3

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

Это не элегантно, но имеет несколько преимуществ ...

  1. Соответствие существующей структуре будет проще для всех, кто будет работать над этой схемой, прежде чем она будет выпущена на пастбище.
  2. У вас нет пропущенных полей, и вы не просите БД сопоставить вещи, которые не существуют.
  3. Поскольку каждая таблица будет только одна, и будет относительно легко создать представление, которое связывает все данные для ваших подпрограмм.

Я уверен, что многие другие не согласятся с этим подходом, но основные цели нормализации и передовой практики - сделать код более согласованным, чтобы его было проще отслеживать и отлаживать ... и, очевидно, доведение всей базы кода до нуля, вероятно, неосуществимо.

Джеймс Снелл
источник
Проблема с этим подходом заключается в том, что база данных не может обеспечить уникальность идентификаторов почтовой рассылки, что является основной целью сопоставления в первую очередь: в противном случае можно сопоставить отдельные поля идентификаторов из каждой таблицы с индикатором «пользовательский тип». сделано без каких-либо изменений.
Близнецы Домино
Я понимаю, к чему вы клоните, но, поработав над такой системой, я дал вариант, который вы, возможно, не рассматривали. Насколько я понимаю, почтовому идентификатору потребуется какое-то содержимое для ссылки куда-либо (что было отправлено по почте или как найти документ), поэтому почтовый идентификатор должен быть в любом случае внешним ключом, что означает, что проблемы уникальности будут решаться в другом месте. Когда я это читаю, таблицы данных ученика и специалиста по администрированию могут иметь разные структуры, поэтому я не вижу поля типа пользователя, добавляющего значение. Первоначальные разработчики должны были решить эту проблему, что они сделали?
Джеймс Снелл
Поле «тип пользователя» будет определять, какую таблицу связать с этой конкретной записью. Это должно быть обработано на уровне приложения в любом случае, и поскольку они находятся в разных таблицах, нет хорошего способа сделать это ограничением внешнего ключа. Разработчики оригинала, похоже, вообще не рассматривали эту проблему, поэтому она превращается в такой беспорядок. :)
GeminiDomino