Руководство по пониманию обычных форм (на простом английском языке).

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

Нормализация стремится моделировать отношения в данных таким образом, чтобы было легко следовать, легко комбинировать и легко поддерживать. Нормализация — это процесс разделения разрозненной паутины данных и организации ее в разумную структуру, руководствуясь набором правил, относящихся к отношениям атрибутов. Эти правила, изложенные пионером реляционных баз данных Эдгаром Ф. Коддом, называются «нормальными формами».

Нормальные формы

Первая нормальная форма

В первой нормальной форме все атрибуты в наборе записей неделимы и атомарны. Ни одно поле не может содержать более одной части связанной информации. Давайте посмотрим на пример. Рассмотрим следующее соотношение:

+===========+===============+=====================================+
| staff_num |  staff_name   |             staff_email             |
+===========+===============+=====================================+
|       123 | John Doe      | [email protected],[email protected]   |
+-----------+---------------+-------------------------------------+
|       456 | Jane Doe      | [email protected],[email protected] |
+-----------+---------------+-------------------------------------+
|       789 | Robert Tables | [email protected]        |
+-----------+---------------+-------------------------------------+

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

Чтобы перейти к первой нормальной форме, нам нужно разделить столбец staff_email из отношения, оставив два связанных отношения.

+===========+===============+
| staff_num |  staff_name   |
+===========+===============+
|       123 | John Doe      |
+-----------+---------------+
|       456 | Jane Doe      |
+-----------+---------------+
|       789 | Robert Tables |
+-----------+---------------+
+===========+==============================+
| staff_num |         staff_email          |
+===========+==============================+
|       123 | [email protected]          |
+-----------+------------------------------+
|       123 | [email protected]          |
+-----------+------------------------------+
|       456 | [email protected]              |
+-----------+------------------------------+

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

Примечание. Кстати, приведенное выше отношение также соответствует третьей нормальной форме.

Вторая нормальная форма

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

Рассмотрим следующее отношение команда/роль с потенциальным ключом (project_num, team).

+=============+========+=================+===============+
| project_num |  team  |      role       |    team_hq    |
+=============+========+=================+===============+
|         123 | Team 1 | User Interface  | New York      |
+-------------+--------+-----------------+---------------+
|         123 | Team 2 | Database Design | San Francisco |
+-------------+--------+-----------------+---------------+
|         465 | Team 2 | API Development | San Francisco |
+-------------+--------+-----------------+---------------+

Значения в столбце team_hq зависят только от атрибута team ключа-кандидата. Они никак не зависят от номера проекта. Чтобы перевести это во вторую нормальную форму, нам нужно разделить столбец team_hq из отношения, оставив два связанных отношения.

+=============+========+=================+
| project_num |  team  |      role       |
+=============+========+=================+
|         123 | Team 1 | User Interface  |
+-------------+--------+-----------------+
|         123 | Team 2 | Database Design |
+-------------+--------+-----------------+
|         465 | Team 2 | API Development |
+-------------+--------+-----------------+
|         789 | Team 2 | User Interface  |
+-------------+--------+-----------------+
+========+===============+
|  team  |    team_hq    |
+========+===============+
| Team 1 | New York      |
+--------+---------------+
| Team 2 | San Francisco |
+--------+---------------+

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

Третья нормальная форма

Чтобы отношение находилось в третьей нормальной форме, таблица должна сначала находиться во второй нормальной форме, а все атрибуты должны определяться только потенциальным ключом. Другими словами, поле должно быть непосредственно связано с ключом-кандидатом. Отношения будут свободны от лишнего содержания.

Рассмотрим следующее отношение персонала/менеджера с суррогатным ключом (staff_num).

+===========+===============+=============+===============+
| staff_num |  staff_name   | manager_num | manager_name  | +===========+===============+=============+===============+
|       123 | John Doe      |         987 | Sara Manageer |
+-----------+---------------+-------------+---------------+
|       456 | Jane Doe      |         654 | Jay Deboss    |
+-----------+---------------+-------------+---------------+
|       789 | Robert Tables |         321 | Elle Hefe     |
+-----------+---------------+-------------+---------------+

В то время как staff_name и manager_num напрямую относятся к суррогатному ключу staff_num, manager_name — нет.

Чтобы перевести это в третью нормальную форму, нам нужно разделить столбец manager_name из отношения, оставив два связанных отношения.

+===========+===============+=============+
| staff_num |  staff_name   | manager_num |
+===========+===============+=============+
|       123 | John Doe      |         987 |
+-----------+---------------+-------------+
|       456 | Jane Doe      |         654 |
+-----------+---------------+-------------+
|       789 | Robert Tables |         321 |
+-----------+---------------+-------------+

+=============+===============+
| manager_num | manager_name  |
+=============+===============+
|         987 | Sara Manageer |
+-------------+---------------+
|         654 | Jay Deboss    |
+-------------+---------------+
|         321 | Elle Hefe     |
+-------------+---------------+

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

Нормальная форма Бойса-Кодда

Чтобы отношение находилось в нормальной форме Бойса-Кодда, таблица должна сначала быть в третьей нормальной форме. Кроме того, он должен быть свободен от обратных зависимостей. Другими словами, если два атрибута в отношении имеют отношение Многие:Один, мы можем вывести множество из одного.

Примечание. Нормальная форма Бойса-Кодда находится где-то между третьей и четвертой нормальными формами. Думайте об этом как о нормальной форме три с половиной.

Рассмотрим отношение следующего сотрудника к навыкам и языку с суррогатным ключом (staff_num).

+===========+============+====================+
| staff_num |  language  |       skill        |
+===========+============+====================+
|       123 | JavaScript | User Interface     |
+-----------+------------+--------------------+
|       123 | JavaScript | Data Visualization |
+-----------+------------+--------------------+
|       456 | Python     | Data Science       |
+-----------+------------+--------------------+
|       789 | Python     | API Development    |
+-----------+------------+--------------------+

В нашей гипотетической компании руководство потребовало, чтобы все действия с пользовательским интерфейсом и визуализацией данных выполнялись с использованием JavaScript. Вся наука о данных и разработка API выполняются на Python. Хотя мы знаем, что язык может иметь несколько компонентов, навык приписывается одному и только одному языку. Мы можем почерпнуть язык из умения.

+===========+====================+
| staff_num |       skill        |
+===========+====================+
|       123 | User Interface     |
+-----------+--------------------+
|       123 | Data Visualization |
+-----------+--------------------+
|       456 | Data Science       |
+-----------+--------------------+
|       456 | Data Visualization |
+-----------+--------------------+
|       789 | API Development    |
+-----------+--------------------+
+============+====================+
|  language  |       skill        |
+============+====================+
| JavaScript | User Interface     |
+------------+--------------------+
| JavaScript | Data Visualization |
+------------+--------------------+
| Python     | Data Science       |
+------------+--------------------+
| JavaScript | Data Visualization |
+------------+--------------------+
| Python     | API Development    |
+------------+--------------------+

Через присоединения мы можем выучить язык члена команды по его навыкам.

Четвертая нормальная форма

Чтобы отношение находилось в четвертой нормальной форме, таблица должна сначала быть в третьей нормальной форме. Отношения должны быть свободны от многозначных зависимостей. Другими словами, запросы не должны оставлять никакой двусмысленности или подразумевать какие-либо результаты.

Примечание. Четвертая нормальная форма встречается нечасто.

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

+=============+===========+====================+
| project_num | staff_num |    project_need    |
+=============+===========+====================+
|       12345 |       123 | User Interface     |
+-------------+-----------+--------------------+
|       12345 |       456 | Data Science       |
+-------------+-----------+--------------------+
|       46578 |       789 | API Development    |
+-------------+-----------+--------------------+
|       78901 |       123 | Data Visualization |
+-------------+-----------+--------------------+

Если бы мы запросили проект 12345, мы бы получили кортежи, которые подразумевают, что сотрудник выполняет определенную роль. Мы получили бы (132, Пользовательский интерфейс) и (456, Наука о данных).

Чтобы сделать это менее двусмысленным и удалить последствия, разделите его на два отношения.

+=============+===========+
| project_num | staff_num |
+=============+===========+
|       12345 |       123 |
+-------------+-----------+
|       12345 |       456 |
+-------------+-----------+
|       46578 |       789 |
+-------------+-----------+
|       78901 |       123 |
+-------------+-----------+
+=============+====================+
| project_num |    project_need    |
+=============+====================+
|       12345 | User Interface     |
+-------------+--------------------+
|       12345 | Data Science       |
+-------------+--------------------+
|       45678 | API Development    |
+-------------+--------------------+
|       78901 | Data Visualization |
+-------------+--------------------+

Затем мы можем объединить две таблицы по номеру проекта, чтобы получить персонал и потребности проекта.

Пятая нормальная форма

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

Примечание. Пятая нормальная форма встречается нечасто.

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

+=============+===========+====================+
| project_num | staff_num |   project_asset    |
+=============+===========+====================+
|       12345 |       123 | User Interface     |
+-------------+-----------+--------------------+
|       12345 |       456 | Data Science       |
+-------------+-----------+--------------------+
|       45678 |       789 | API Development    |
+-------------+-----------+--------------------+
|       78901 |       123 | Data Visualization |
+-------------+-----------+--------------------+

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

Мы можем обойти это, объединив три отдельных отношения. Один персонал для проекта, один навык для персонала и один навык для проекта.

+=============+===========+
| project_num | staff_num |
+=============+===========+
|       12345 |       123 |
+-------------+-----------+
|       12345 |       456 |
+-------------+-----------+
|       45678 |       789 |
+-------------+-----------+
|       78901 |       123 |
+-------------+-----------+
+===========+====================+
| staff_num |   project_asset    |
+===========+====================+
|       123 | User Interface     |
+-----------+--------------------+
|       456 | Data Science       |
+-----------+--------------------+
|       789 | API Development    |
+-----------+--------------------+
|       123 | Data Visualization |
+-----------+--------------------+
+=============+====================+
| project_num |   project_asset    |
+=============+====================+
|       12345 | User Interface     |
+-------------+--------------------+
|       12345 | Data Science       |
+-------------+--------------------+
|       45678 | API Development    |
+-------------+--------------------+
|       78901 | Data Visualization |
+-------------+--------------------+

Какую форму следует использовать?

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

Эта статья первоначально была опубликована как презентация Software Engineering Birds of a Feather в Международном центре науки о данных RTI.