Поместите данные туда, где им место
Также доступно на 28 языках
English · Español · 日本語 · Português (Brasil) · Français · Deutsch · Italiano · 한국어 · 简体中文 · Português (Portugal) · 繁體中文 · Bahasa Indonesia · Türkçe · Polski · Tiếng Việt · ไทย · Nederlands · العربية · עברית · Українська · हिन्दी · Svenska · Dansk · Norsk bokmål · Suomi · Español (México) · Français (Canada) · Deutsch (Schweiz)

Модуль 4 из 6 главы Моделирование данных ради смысла, глава 2 курса Data Architect. Каждый модуль самостоятелен. Назад: Постройте логическую модель · Далее: Проектируйте до третьей нормальной формы, затем отступите
Вопрос, на который отвечает этот модуль: Как нормализация помогает понять, где на самом деле место данным?
У нас есть логическая модель: сущности, связи, атрибуты и ключи. Теперь я хочу задать один из самых важных вопросов моделирования данных: действительно ли этим данным место здесь?
Звучит просто. Но это не так. Я видел множество систем, где каждое поле имело смысл в тот момент, когда его кто-то создал. Проблема была не в том, что поля нелепые, а в том, что факты хранились не в том месте. Доставка хранит информацию, которая на самом деле описывает распределительный центр. Один и тот же адрес встречается в сотнях записей. Одно поле содержит три значения через запятую. Изменение одного бизнес-факта означает обновление 10 000 записей. Именно эту проблему помогает решить нормализация, и прежде чем мы утонем в терминологии баз данных, вот вся идея целиком: поместите факт туда, где ему место. Все остальное в этом модуле помогает делать это более систематично.
Что такое нормализация?
Заголовок раздела «Что такое нормализация? »Нормализация — это такая организация данных, при которой каждый факт хранится один раз, в том месте, где ему место. Когда одна таблица делает слишком много работ, мы разбиваем ее на таблицы, каждая из которых описывает одну вещь, и соединяем их ключами.
Зачем это нужно? Потому что факт, который хранится один раз, меняется один раз. Нормализация предотвращает избыточность — один и тот же факт, скопированный во множество записей, — а вместе с ней и несогласованность, которая заползает, когда одну копию обновили, а другие нет. Поэтому она подходит транзакционным системам вроде Salesforce, где записи создаются и обновляются весь день.
Мы нормализуем шагами, которые называются нормальными формами, и каждая задает один вопрос:
- Первая нормальная форма (1НФ): одно значение в поле и что-то, что однозначно определяет каждую строку.
- Вторая нормальная форма (2НФ): каждый факт описывает все целиком, что определяет строку, а не только часть этого.
- Третья нормальная форма (3НФ): каждый факт описывает эту вещь, а не что-то другое, с чем она связана.
За третьей есть и другие формы. Я скажу о них пару слов позже, а также о том, почему мы обычно останавливаемся на третьей. Сначала давайте посмотрим, как проходят три шага.
Зачем мы нормализуем?
Заголовок раздела «Зачем мы нормализуем? »Когда я веду это занятие вживую, я начинаю не с доставок. Я начинаю с собак. Собак понимают все, поэтому бизнес никого не отвлекает, и мы можем наблюдать, как данные пытаются найти свой дом. Потом мы переносим те же ходы на Steve Trucking.
Сидеть, стоять, моделировать
Заголовок раздела «Сидеть, стоять, моделировать »Начало: сырая таблица. Я вывожу на экран пару собак, а потом аудитория добавляет своих. За несколько минут у нас получается один большой список: собака, год рождения, кто владелец, где живет владелец, порода, кто ее вывел и какие трюки она знает.
| Собака | Год рождения | Владелец | Индекс | Город | Порода | Заводчик | Трюки |
|---|---|---|---|---|---|---|---|
| Bella | 2017 | Marcus | 80202 | Денвер | Лабрадор | Maple Ridge | Сидеть, Стоять, Дай лапу |
| Milo | 2019 | Marcus | 80202 | Денвер | Пудель | Golden Meadow | Сидеть, Перевернись |
| Max | 2022 | Ana | 64105 | Канзас-Сити | Метис | Сидеть, Умри | |
| Luna | 2020 | Kenji | 98101 | Сиэтл | Хаски | Cascade | Сидеть, Стоять, Кружись |
| Rocky | 2024 | Priya | 30301 | Атланта | Бигль | Maple Ridge | Сидеть |
| Coco | 2014 | Ana | 64105 | Канзас-Сити | Чихуахуа | Little Paws | Дай лапу, Кружись |
Это работает. В первый день работает любая таблица. Но посмотрите, что уже не так. Как найти всех собак, которые знают «Дай лапу»? Индекс Ana есть в двух строках, и индекс Marcus тоже: что будет, если кто-то обновит одну строку и не обновит другую? Maple Ridge встречается дважды, Канзас-Сити тоже. А Max взяли из приюта, так что заводчика нет вовсе.
Шаг 1: первая нормальная форма. Два правила. Каждое поле содержит одно значение, так что список трюков через запятую должен уйти. И каждой строке нужно что-то, что однозначно ее определяет. Не кличка собаки: собак по кличке Max очень много. ID: автоматически сгенерированный ID или GUID. Трюки выносятся в отдельные строки, по одной строке на каждую собаку и каждый трюк. А когда у трюка есть своя строка, появляется место для того, что никогда не скажешь в списке через запятую: насколько трюк сложный, насколько хорошо эта собака его выполняет и когда она его выучила.
Собака (определяется по ID собаки)
| ID собаки | Кличка | Год рождения | Владелец | Индекс | Город | Порода | Заводчик |
|---|---|---|---|---|---|---|---|
| D-001 | Bella | 2017 | Marcus | 80202 | Денвер | Лабрадор | Maple Ridge |
| D-002 | Milo | 2019 | Marcus | 80202 | Денвер | Пудель | Golden Meadow |
| D-003 | Max | 2022 | Ana | 64105 | Канзас-Сити | Метис | |
| D-004 | Luna | 2020 | Kenji | 98101 | Сиэтл | Хаски | Cascade |
| D-005 | Rocky | 2024 | Priya | 30301 | Атланта | Бигль | Maple Ridge |
| D-006 | Coco | 2014 | Ana | 64105 | Канзас-Сити | Чихуахуа | Little Paws |
Трюк собаки (определяется по ID собаки + трюку)
| ID собаки | Трюк | Сложность | Уровень навыка | Выучен |
|---|---|---|---|---|
| D-001 | Сидеть | Легкий | Эксперт | 2017-11 |
| D-001 | Стоять | Легкий | Эксперт | 2018-01 |
| D-001 | Дай лапу | Средний | Хорошо | 2018-06 |
| D-002 | Сидеть | Легкий | Эксперт | 2020-02 |
| D-002 | Перевернись | Сложный | Учится | 2023-05 |
| D-003 | Сидеть | Легкий | Хорошо | 2022-06 |
| D-003 | Умри | Сложный | Учится | 2024-03 |
| D-004 | Сидеть | Легкий | Эксперт | 2020-11 |
| D-004 | Стоять | Легкий | Хорошо | 2021-01 |
| D-004 | Кружись | Средний | Эксперт | 2021-06 |
| D-005 | Сидеть | Легкий | Учится | 2025-02 |
| D-006 | Дай лапу | Средний | Эксперт | 2015-06 |
| D-006 | Кружись | Средний | Учится | 2025-04 |
Теперь «все собаки, которые знают „Дай лапу“» — простой вопрос. Но обратите внимание, по чему определяется Трюк собаки: не по одной вещи, а по двум вместе — собаке и трюку. Запомните это.
Шаг 2: вторая нормальная форма. Вторая нормальная форма важна, только когда строка определяется сочетанием, а Трюк собаки именно такой. Поэтому о каждом столбце я спрашиваю: описывает ли он сочетание целиком или только его часть? Уровень навыка описывает эту собаку, выполняющую этот трюк: Bella — эксперт в «Сидеть», Rocky еще учится. «Выучен» — то же самое. А сложность? «Сидеть» — легкий трюк, какая бы собака его ни выполняла. Сложность описывает трюк, то есть только часть ключа, и именно поэтому «Легкий» повторяется в каждой строке с «Сидеть». Поэтому трюк получает свою таблицу, а остается пара.
Трюк
| ID трюка | Трюк | Сложность |
|---|---|---|
| T-01 | Сидеть | Легкий |
| T-02 | Стоять | Легкий |
| T-03 | Дай лапу | Средний |
| T-04 | Перевернись | Сложный |
| T-05 | Умри | Сложный |
| T-06 | Кружись | Средний |
Трюк собаки (ID собаки + ID трюка)
| ID собаки | ID трюка | Уровень навыка | Выучен |
|---|---|---|---|
| D-001 | T-01 | Эксперт | 2017-11 |
| D-001 | T-02 | Эксперт | 2018-01 |
| D-001 | T-03 | Хорошо | 2018-06 |
| D-002 | T-01 | Эксперт | 2020-02 |
| D-002 | T-04 | Учится | 2023-05 |
| D-003 | T-01 | Хорошо | 2022-06 |
| D-003 | T-05 | Учится | 2024-03 |
| D-004 | T-01 | Эксперт | 2020-11 |
| D-004 | T-02 | Хорошо | 2021-01 |
| D-004 | T-06 | Эксперт | 2021-06 |
| D-005 | T-01 | Учится | 2025-02 |
| D-006 | T-03 | Эксперт | 2015-06 |
| D-006 | T-06 | Учится | 2025-04 |
Эта таблица Трюк собаки — та самая, которую мы нашли в модуле 2, и в моделировании данных у нее есть название: связующая сущность (junction), или, как я ее называю, логический связующий объект. Собака знает много трюков; трюк знают многие собаки. Связующая сущность стоит между ними, и это не пустая сантехника: она хранит факты, которые принадлежат самой связи, — уровень навыка и дату. Мы встретим ее снова в модуле 6, а в главе 3 увидим, что Salesforce понимает «junction object» уже: связующий объект Salesforce (Salesforce Junction Object) — это пользовательский объект с двумя связями master-detail (глава 3, модуль 7).
Шаг 3: третья нормальная форма. Теперь вернемся к таблице Собака. Все в ней определяется по ID собаки, так что второй нормальной форме сказать нечего. Третья нормальная форма задает другой вопрос: описывает ли этот факт собаку или что-то другое, с чем собака связана? Порода — самостоятельная вещь: лабрадор есть лабрадор, какая бы это ни была собака, и факты о нем, например размер, принадлежат породе, а не Bella. Индекс и город описывают владельца, а не собаку: там живет Ana, на какую бы из ее собак мы ни смотрели. Данные заводчика описывают заводчика. Каждое из этого — факт о другом факте, поэтому каждое уходит в свою таблицу, а собака ссылается на нее внешним ключом:
- Собака: ID собаки, Кличка, Год рождения, ID владельца, ID породы, ID заводчика
- Владелец: ID владельца, Имя, Индекс, Город
- Порода: ID породы, Порода, Размер
- Заводчик: ID заводчика, Название, Город
- Трюк и Трюк собаки — из шага 2
Теперь Ana хранится один раз, и обе ее собаки ссылаются на нее. Maple Ridge хранится один раз, а таблица Порода дает дом фактам о каждой породе, например размеру, которым в таблице было негде жить. Внешний ключ может быть пустым: у Max нет ID заводчика, и это честный ответ, а не пробел. Потом я задаю аудитории вопрос с подвохом: когда владелец получил собаку — куда идет эта дата? К собаке. В этой модели у собаки в каждый момент один владелец, так что эта дата описывает связь собаки с ее владельцем.
Можно пойти на шаг дальше и отделить город от индекса; в учебниках так и делают. На практике адрес держат вместе, да и Канзас-Сити все равно находится в двух штатах — хорошее напоминание, что реальный мир наших учебников не читает.
Вот и вся последовательность: таблица, затем три вопроса, и каждый факт оказывается в одном месте. Отойдите на шаг и посмотрите, что мы построили. Один беспорядочный список стал шестью таблицами, каждая из которых описывает одну вещь, и все они соединены ключами:
Чтобы за этим проследить, никому не понадобилось ни одного слова из Salesforce. Это просто собаки, люди вокруг них и то, что они умеют.
Теперь применим это к Steve Trucking
Заголовок раздела «Теперь применим это к Steve Trucking »На собаках эти ходы легко увидеть. На настоящем бизнесе они начинают иметь значение, потому что в масштабе те же ошибки стоят настоящих денег. Так что зададим те же три вопроса Steve Trucking. Представьте, что компания хранит свои доставки вот так:
| Доставка | Розничный партнер | Распределительный центр | Город DC | Бригада | Грузовик |
|---|---|---|---|---|---|
| 1001 | Keystone | Keystone Phoenix DC | Финикс | Бригада 12 | 117 |
| 1002 | Keystone | Keystone Phoenix DC | Финикс | Бригада 8 | 122 |
| 1003 | Keystone | Keystone Phoenix DC | Финикс | Бригада 12 | 117 |
На первый взгляд здесь нет ничего сложного. Но посмотрите на распределительный центр. Мы повторяем «Keystone Phoenix DC, Финикс» в каждой доставке, которая из него выезжает: Steve Trucking делает около 3300 доставок в день через девять DC, то есть по несколько сотен в день из каждого. Что будет, если этот DC переедет? Сколько записей мы меняем и что будет, если одну пропустят? Теперь данные говорят, что DC находится в двух городах одновременно. Это аномалия обновления. А если мы хотим завести новый DC до его первой доставки? А если мы удалим последнюю доставку из DC: не удалили ли мы только что единственное место, где хранили что-либо о нем?
Это разные симптомы одной проблемы: мы смешали факты о разных вещах. Факты о распределительном центре принадлежат распределительному центру. Факты о доставке — доставке. Факты о бригаде — бригаде. Вот что такое нормализация на практике.
Первая нормальная форма: не прячьте повторяющуюся структуру
Заголовок раздела «Первая нормальная форма: не прячьте повторяющуюся структуру »Начнем с первой нормальной формы, или 1НФ, где люди иногда делают нормализацию сложнее, чем нужно.
Допустим, у доставки есть поле «Позиции»: «Холодильник, комплект ледогенератора, вывоз старой техники». Мы положили три куска информации в одно поле. Кажется удобным — пока кто-нибудь не спросит: «Сколько вывозов мы сделали в прошлом месяце?» Или представьте поля «Позиция 1», «Позиция 2» и «Позиция 3». Это работает — до доставки с четырьмя позициями. Проблема не в том, что мы выбрали не то число полей. Проблема в том, что мы не распознали повторяющуюся структуру. В доставке может быть несколько позиций. Это данные, которые нужно смоделировать: та самая позиция доставки, которую мы нашли в модуле 2.
Практический урок 1НФ: не прячьте повторяющуюся структуру внутри полей. Если бизнес говорит вам, что что-то может происходить много раз, прислушайтесь. Возможно, там пытается выбраться наружу еще одна сущность.
В реальных org вы будете видеть это постоянно. Кому-то нужно описание на нескольких языках, и он создает поле Description_English, потом Description_French, потом Description_German. Или для согласования нужно несколько человек, и появляются «Согласующий 1», «Согласующий 2», «Согласующий 3». Когда я нахожу такое на ревью, это шлепок по рукам: исправляйте модель данных. Одно-два — возможно, если у бизнеса действительно всегда бывает только один или два. Все, что больше, должно быть дочерним объектом: перевод с языком, шаг согласования с согласующим. Следующий язык или четвертый согласующий становится новой записью, а не новым полем и новым развертыванием.
Вторая нормальная форма: описывайте вещь целиком
Заголовок раздела «Вторая нормальная форма: описывайте вещь целиком »Вторая нормальная форма, или 2НФ, важна, когда запись определяется сочетанием вещей. Возьмем позицию доставки. У нее свой ID записи, но каждая уникальна по сочетанию доставка + продукт. Допустим, мы храним доставку, продукт, количество и описание продукта.
Количеству здесь место: оно описывает этот продукт в этой доставке. А описание продукта описывает не сочетание, а продукт. Поместите его в каждую позицию доставки — и мы будем повторять его каждый раз, когда эта модель холодильника уезжает на очередном грузовике. Это подсказка, что факту место где-то еще.
Итак, вопрос 2НФ: описывает ли этот атрибут всю вещь, которую я определяю, или только ее часть? Если он описывает только продукт, он идет к продукту. Если он описывает место продукта в этой конкретной доставке (количество, устанавливается ли он), ему место в позиции доставки. Многие администраторы никогда сознательно не скажут: «Сейчас я применяю вторую нормальную форму». И это нормально. Для меня гораздо важнее, чтобы вы научились спрашивать: что на самом деле описывает этот факт?
Третья нормальная форма: не храните факты о других фактах
Заголовок раздела «Третья нормальная форма: не храните факты о других фактах »А теперь та, что для меня важнее всего в повседневном моделировании: третья нормальная форма, или 3НФ.
У каждого распределительного центра есть руководитель на стороне розничного партнера — человек, который пользуется партнерским порталом. Допустим, мы записываем имя руководителя DC прямо в каждую доставку. Что произойдет, когда в DC сменится руководитель? Мы обновим каждую доставку из этого DC. Это снова тот же запашок: мы повторяем факт, которому место где-то еще. Доставка выезжает из DC; у DC есть руководитель. Это две связи, и имени руководителя место у DC.
Классический учебный пример — адрес: почтовый индекс, город и штат хранятся вместе в каждой записи, хотя город и штат обычно следуют из индекса. Та же форма, тот же вопрос.
Практический вопрос 3НФ: описывает ли этот факт эту вещь или что-то другое, с чем эта вещь связана? Я хочу, чтобы вы задавали его постоянно.
Теперь отойдите на шаг, так же как мы сделали с собаками. Таблица доставок, с которой мы начали, превратилась в пять вещей, и у каждой свои факты: позиция доставки вышла на первой нормальной форме, продукт — на второй, а город и руководитель распределительного центра переехали в распределительный центр на третьей.
Те же три вопроса, тот же результат: каждый факт в одном месте.
Простой способ думать о 1НФ, 2НФ и 3НФ
Заголовок раздела «Простой способ думать о 1НФ, 2НФ и 3НФ »Нормализации можно посвятить целый курс по базам данных. Мы этого делать не будем. Для этого курса я хочу, чтобы у вас было рабочее понимание архитектора:
- 1НФ: не прячу ли я несколько значений или повторяющиеся структуры внутри поля или записи?
- 2НФ: описывает ли этот факт всю вещь, которую я определяю?
- 3НФ: действительно ли этот факт описывает эту сущность или что-то, связанное с ней?
Это не определения для учебника по базам данных. Это вопросы, которыми я действительно могу пользоваться, пока проектирую.
Да, нормализация идет дальше: нормальная форма Бойса — Кодда (BCNF) ужесточает правила 3НФ для моделей с более сложными ключами, а еще есть четвертая, пятая и даже шестая нормальные формы. Они имеют дело с более редкими видами повторения — теми, что возникают из необычных структур ключей, а не из повседневных бизнес-записей. Много теории, и по моему опыту она не стоит головной боли для тех систем, которые мы строим на Salesforce. В бизнес-системах давление обычно направлено в другую сторону: мы гораздо чаще отступаем от третьей нормальной формы — ради пользователей, отчетности и производительности, — чем идем дальше нее. Это следующий модуль. Знайте, что дальнейшие формы существуют, но для большей части нашей работы 1НФ–3НФ дают нужные инструменты мышления.
Проектируйте под третью нормальную форму
Заголовок раздела «Проектируйте под третью нормальную форму »Вот моя позиция. Когда я пытаюсь понять бизнес и спроектировать его логическую модель, я хочу начать с чистого листа: факты о доставке — у доставки, факты о распределительном центре — у DC, факты о грузовике — у грузовика, повторяющиеся понятия смоделированы как понятия. Я не хочу, чтобы бизнес-факт копировался по всей модели только потому, что так, возможно, будет проще сделать экран или отчет. Пока нет.
Но нормализация — не религия. Цель — не максимально академически чистая модель. Я видел архитекторов, настолько сосредоточенных на чистоте, что модель становилась мучением для всех, кому приходилось с ней работать. Мы не пытаемся выиграть конкурс по нормализации; мы строим систему, а у системы есть пользователи, отчеты, интеграции, требования к производительности, масштаб и все чаще ИИ, который читает данные. Им может понадобиться другая форма данных.
Поэтому моя отправная точка — проектировать до третьей нормальной формы, где это осуществимо, а затем отступать, когда необходимо: сначала понять, где место каждому факту. Затем, если мы собираемся от этого отойти, я хочу знать почему. Если ответ — «потому что я не знал, куда еще положить поле», этого недостаточно. Если ответ — «мы намеренно скопировали это значение, потому что так оценочная карта партнера становится простой, и у нас есть надежный способ держать его синхронизированным», — вот теперь у нас архитектурный разговор.
Что здесь решает архитектор
Заголовок раздела «Что здесь решает архитектор »Нормализуя логическую модель, я ищу:
- Повторяющиеся значения или структуры, которые нужно смоделировать отдельно.
- Атрибуты, которые описывают другую сущность, а не ту, в которой они хранятся.
- Факты, без необходимости продублированные во множестве записей, и зависимости, из-за которых они становятся несогласованными.
- Бизнес-понятия, спрятанные внутри полей.
- Чистую модель, которая отражает истину до любых компромиссов реализации.
Не потому, что каждая физическая система обязана оставаться идеально нормализованной. Совсем наоборот. В следующем модуле мы намеренно отойдем от чистой модели — ради пользователей, отчетности, масштаба, аналитики и ИИ — и сделаем это, точно зная, чем жертвуем.
