Розмістіть дані там, де їм місце
Також доступно 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 записів. Саме цю проблему допомагає розв’язати нормалізація, і перш ніж ми потонемо в термінології баз даних, ось уся ідея: розмістіть факт там, де йому місце. Усе інше в цьому модулі допомагає робити це системніше.
Що таке нормалізація?
Section titled “Що таке нормалізація? ”Нормалізація — це впорядкування даних так, щоб кожен факт зберігався один раз, у тому місці, де йому належить бути. Коли одна таблиця виконує забагато завдань, ми розділяємо її на таблиці, кожна з яких описує одну річ, і з’єднуємо їх ключами.
Навіщо це? Бо факт, збережений один раз, — це факт, який ми змінюємо один раз. Нормалізація запобігає надлишковості (redundancy), тобто копіюванню того самого факту в багато записів, а разом із нею й невідповідностям, що закрадаються, коли одну копію оновили, а інші — ні. Ось чому вона пасує транзакційним системам на кшталт Salesforce, де записи створюються й оновлюються цілий день.
Ми нормалізуємо кроками, які називаються нормальними формами, і кожна ставить одне запитання:
- Перша нормальна форма (1НФ): одне значення в полі і щось, що унікально ідентифікує кожен рядок.
- Друга нормальна форма (2НФ): кожен факт описує все, що ідентифікує рядок, а не лише його частину.
- Третя нормальна форма (3НФ): кожен факт описує саме цю річ, а не щось інше, з чим вона пов’язана.
Є й форми після третьої. Я скажу про них кілька слів пізніше, а також про те, чому ми зазвичай зупиняємося на третій. Спершу подивімося, як відбуваються ці три кроки.
Навіщо ми нормалізуємо?
Section titled “Навіщо ми нормалізуємо? ”Коли я викладаю це наживо, я не починаю з доставок. Я починаю із собак. Собак розуміють усі, тож ніхто не відволікається на бізнес, і ми можемо спостерігати, як дані шукають свій дім. Потім ми переносимо ті самі кроки на Steve Trucking.
Сидіти, чекати, моделювати
Section titled “Сидіти, чекати, моделювати ”Початок: сира таблиця. Я виводжу на екран кількох собак, а потім аудиторія додає своїх. За кілька хвилин у нас один великий список: собака, рік народження, хто власник, де живе власник, порода, хто її вивів і які трюки вона знає.
| Собака | Народження | Власник | Індекс | Місто | Порода | Заводчик | Трюки |
|---|---|---|---|---|---|---|---|
| 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
Section titled “Тепер застосуймо це до 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 робить близько 3 300 доставок на день із дев’яти DC, тож по кількасот на день із кожного. Що станеться, якщо цей DC переїде? Скільки записів ми змінимо і що буде, якщо один пропустимо? Тепер дані кажуть, що DC перебуває у двох містах одночасно. Це аномалія оновлення. А якщо ми хочемо завести новий DC до його першої доставки? А якщо видалимо останню доставку з DC — чи не видалили ми щойно єдине місце, де зберігали про нього хоч щось?
Це різні симптоми однієї проблеми: ми змішали докупи факти про різні речі. Факти про розподільчий центр належать розподільчому центру. Факти про доставку належать доставці. Факти про бригаду належать бригаді. Ось що таке нормалізація на практиці.
Перша нормальна форма: не ховайте повторювану структуру
Section titled “Перша нормальна форма: не ховайте повторювану структуру ”Почнімо з першої нормальної форми, або 1НФ, де люди іноді роблять нормалізацію складнішою, ніж треба.
Припустімо, у доставки є поле «Позиції»: «Холодильник, комплект льодогенератора, вивезення старого». Ми поклали три шматки інформації в одне поле. Здається зручним, доки хтось не спитає: «Скільки вивезень ми зробили минулого місяця?» Або уявіть поля «Позиція 1», «Позиція 2» і «Позиція 3». Це працює — до доставки з чотирма позиціями. Проблема не в тому, що ми обрали неправильну кількість полів «Позиція». Проблема в тому, що ми не розпізнали повторювану структуру. Доставка може мати кілька позицій. Це дані, які варто змоделювати: Позиція доставки, яку ми знайшли в модулі 2.
Практичний урок 1НФ: не ховайте повторювану структуру всередині полів. Якщо бізнес каже вам, що щось може відбуватися багато разів, — слухайте. Можливо, там є ще одна сутність, яка хоче вийти назовні.
Ви постійно бачитимете це в реальних org. Комусь потрібен опис кількома мовами, тож створюють поле Description_English, потім Description_French, потім Description_German. Або для погодження потрібні кілька людей, і в нас з’являються «Погоджувач 1», «Погоджувач 2», «Погоджувач 3». Коли я знаходжу таке під час рев’ю — по руках, виправляйте модель даних. Одне чи два, можливо, якщо в бізнесу справді завжди лише одне чи два. Усе, що більше, має бути дочірнім об’єктом: Переклад із мовою, Крок погодження з погоджувачем. Наступна мова чи четвертий погоджувач стає новим записом, а не новим полем і деплоєм.
Друга нормальна форма: описуйте всю річ
Section titled “Друга нормальна форма: описуйте всю річ ”Друга нормальна форма, або 2НФ, важлива, коли запис ідентифікується комбінацією речей. Візьмімо Позицію доставки. Вона має власний ID запису, але кожна унікальна за Доставкою + Продуктом. Припустімо, ми зберігаємо доставку, продукт, кількість і опис продукту.
Кількість тут доречна: вона описує цей продукт у цій доставці. Але опис продукту не описує комбінацію; він описує продукт. Покладіть його на кожну Позицію доставки — і ми повторюватимемо його щоразу, коли ця модель холодильника їде іншою вантажівкою. Це підказка, що факт належить деінде.
Тож запитання 2НФ таке: чи описує цей атрибут усю річ, яку я ідентифікую, чи лише її частину? Якщо він описує лише продукт, він іде до Продукту. Якщо він описує місце продукту в цій конкретній доставці (кількість, чи його встановлюють), він належить Позиції доставки. Багато адміністраторів ніколи свідомо не скажуть: «Зараз я застосовую другу нормальну форму». І це нормально. Мені набагато важливіше, щоб ви навчилися питати: що насправді описує цей факт?
Третя нормальна форма: не зберігайте факти про інші факти
Section titled “Третя нормальна форма: не зберігайте факти про інші факти ”Тепер та, яка для мене найважливіша в щоденному моделюванні: третя нормальна форма, або 3НФ.
Кожен розподільчий центр має менеджера з боку роздрібного партнера — людину, яка користується партнерським порталом. Припустімо, ми записуємо ім’я менеджера DC прямо на кожну доставку. Що станеться, коли в DC з’явиться новий менеджер? Ми оновлюємо кожну доставку з цього DC. Знову той самий запах: ми повторюємо факт, який належить деінде. Доставка виходить із DC; у DC є менеджер. Це два зв’язки, і ім’я менеджера належить DC.
Класична підручникова версія — адреса: поштовий індекс, місто і штат зберігаються разом на кожному записі, хоча місто і штат зазвичай випливають з індексу. Та сама форма, те саме запитання.
Практичне запитання 3НФ таке: чи описує цей факт цю річ, чи щось інше, з чим ця річ пов’язана? Я хочу, щоб ви ставили його постійно.
Тепер відступіть, так само як ми зробили із собаками. Таблиця доставок, з якої ми почали, стала п’ятьма речами, і кожна тримає власні факти: Позиція доставки вийшла на першій нормальній формі, Продукт — на другій, а місто і менеджер розподільчого центру перейшли до Розподільчого центру на третій.
Ті самі три запитання, той самий результат: кожен факт в одному місці.
Простий спосіб думати про 1НФ, 2НФ і 3НФ
Section titled “Простий спосіб думати про 1НФ, 2НФ і 3НФ ”Нормалізації можна присвятити цілий курс баз даних. Ми цього робити не будемо. Для цього курсу я хочу, щоб ви мали робоче розуміння архітектора:
- 1НФ: чи не ховаю я кілька значень або повторювані структури всередині поля чи запису?
- 2НФ: чи описує цей факт усю річ, яку я ідентифікую?
- 3НФ: чи справді цей факт описує цю сутність, чи щось, пов’язане з нею?
Це не визначення для підручника з баз даних. Це запитання, якими я справді можу користуватися, коли проєктую.
Так, нормалізація йде далі: нормальна форма Бойса–Кодда (BCNF) посилює правила 3НФ для моделей зі складнішими ключами, а ще є четверта, п’ята і навіть шоста нормальні форми. Вони мають справу з рідкіснішими видами повторення — тими, що виникають більше з незвичних структур ключів, ніж зі звичайних бізнес-записів. Багато теорії, і, з мого досвіду, вона не варта головного болю для тих систем, які ми будуємо на Salesforce. У бізнес-системах тиск зазвичай іде в протилежний бік: ми набагато частіше відступаємо від третьої нормальної форми — заради користувачів, звітності й продуктивності, — ніж ідемо далі за неї. Це тема наступного модуля. Знайте, що подальші форми існують, але для більшості того, що ми робимо, 1НФ–3НФ дають нам потрібні інструменти мислення.
Проєктуйте до третьої нормальної форми
Section titled “Проєктуйте до третьої нормальної форми ”Ось моя позиція. Коли я намагаюся зрозуміти бізнес і спроєктувати його логічну модель, я хочу почати з чистого: факти доставки — з доставкою, факти розподільчого центру — з DC, факти вантажівки — з вантажівкою, а повторювані поняття змодельовані як поняття. Я не хочу, щоб бізнес-факт копіювався по всій моделі лише тому, що це може спростити якийсь екран чи звіт. Поки що ні.
Але нормалізація — не релігія. Мета — не найакадемічніше чиста модель із можливих. Я бачив архітекторів, які так зосереджувалися на чистоті, що модель ставала болем для всіх, хто мав нею користуватися. Ми не намагаємося виграти конкурс нормалізації; ми будуємо систему, а в цієї системи є користувачі, звіти, інтеграції, вимоги до продуктивності, масштаб і, дедалі частіше, ШІ, що читає дані. Їм можуть бути потрібні дані в іншій формі.
Тож моя відправна точка — проєктувати до третьої нормальної форми, де це можливо, а потім відступати, коли це необхідно: спершу зрозуміти, де місце кожного факту. А потім, якщо ми від цього відходимо, я хочу знати чому. Якщо відповідь «бо я не знав, куди ще покласти поле», цього недостатньо. Якщо відповідь «ми свідомо скопіювали це значення, бо воно спрощує оцінну картку партнера, і в нас є надійний спосіб тримати його синхронізованим», — ось тепер у нас розмова про архітектуру.
Що тут вирішує архітектор
Section titled “Що тут вирішує архітектор ”Коли я нормалізую логічну модель, я шукаю:
- Повторювані значення чи структури, які слід моделювати окремо.
- Атрибути, що описують іншу сутність, ніж та, на якій вони зберігаються.
- Факти, без потреби продубльовані в багатьох записах, і залежності, через які вони стають неузгодженими.
- Бізнес-поняття, сховані всередині полів.
- Чисту модель, що відображає правду до будь-яких компромісів реалізації.
Не тому, що кожна фізична система мусить лишатися ідеально нормалізованою. Якраз навпаки. У наступному модулі ми свідомо відійдемо від чистої моделі — заради користувачів, звітності, масштабу, аналітики і ШІ, — і зробимо це, точно знаючи, чим жертвуємо.
