SQL

БЛОК 1. Основы реляционных БД — 3. Нормализация 1NF–3NF

📚 24 вопросовПройти тест →
Лекция

БЛОК 1. Основы реляционных БД — 3. Нормализация 1NF–3NF

SQL

3. Нормализация 1NF–3NF

🧭 Введение: зачем нормализация нужна до первых багов

Когда таблицы проектируют «по ощущениям», в них быстро появляются дубли: один и тот же клиент, город или цена живут в десятках строк.
Пока данных мало, это почти незаметно, но при росте продукта начинаются постоянные расхождения и ручные исправления.
Нормализация 1NF–3NF — это практический способ убрать лишние повторы и разложить данные так, чтобы каждый факт хранился в одном месте.
В итоге схема становится предсказуемой: обновления безопаснее, отчёты точнее, а сопровождение дешевле.
💡 Совет: Воспринимайте нормализацию не как «теорию для экзамена», а как технику снижения будущих багов.
Вывод: Нормализация — это базовая защита от дубликатов, противоречий и дорогих исправлений в продакшене.

⚠️ Проблема -> решение

Без нормализации команда часто хранит всё в одной «широкой» таблице: заказ, клиент, товар, менеджер, город, статус, цена.
Из-за этого изменение одного факта требует править много строк, а удаление может случайно стереть важную информацию.
Решение — проходить данные через 1NF, 2NF и 3NF:
сначала атомарные поля, затем устранение частичных зависимостей, потом удаление транзитивных зависимостей.
🟢 Если совсем просто: Один факт должен иметь одно место хранения.
🎯 Как понять, что этап прошёл успешно: Изменение справочного значения делается в одной таблице и сразу корректно отражается во всех связанных записях.

🛠️ Чем помогает и как работает

Нормализация даёт схеме чёткую структуру и убирает «скрытые мины», которые обычно взрываются при росте данных.
Это не про усложнение, а про системное устранение причин аномалий.
🟢 Если совсем просто: Нормализация делает данные менее хрупкими.
🎯 Как понять, что этап прошёл успешно: В схеме нет лишних дублей, а логика обновлений и удалений предсказуема.
Чем помогает:
  • снижает дублирование данных;
  • уменьшает риск противоречивых значений;
  • упрощает обновления и аудит изменений;
  • делает схему масштабируемой и понятной для команды.
Как это работает:
  • Шаг 1: фиксируем сущности и все атрибуты, которые сейчас лежат в «широкой» таблице.
  • Шаг 2: приводим данные к 1NF (атомарные поля, без списков в ячейках).
  • Шаг 3: определяем ключи и проверяем, от чего функционально зависит каждый атрибут.
  • Шаг 4: применяем 2NF и выносим атрибуты, зависящие только от части составного ключа.
  • Шаг 5: применяем 3NF и убираем транзитивные зависимости через отдельные справочники.
  • Шаг 6: добавляем PK/FK/UNIQUE/CHECK и индексы под ключевые чтения.
  • Шаг 7: прогоняем сценарии insert/update/delete и проверяем, что аномалии исчезли.
  • Шаг 8: фиксируем результат в миграции и дизайн-заметках.
Вывод: Нормализация переводит схему из состояния «работает случайно» в состояние «работает по правилам».

📚 Ключевые термины (простыми словами)

Чтобы не запутаться, важно одинаково понимать термины, которые постоянно используются при разборе 1NF–3NF.
🟢 Если совсем просто: Это словарь, без которого сложно обсуждать зависимости и разбиение таблиц.
🎯 Как понять, что этап прошёл успешно: Вы можете объяснить любой термин на примере своей схемы.
  • 1NF (первая нормальная форма) — все поля атомарны, без массивов/списков в ячейках.
  • 2NF (вторая нормальная форма) — нет частичных зависимостей от составного ключа.
  • 3NF (третья нормальная форма) — нет транзитивных зависимостей между неключевыми полями.
  • Functional Dependency (функциональная зависимость) — значение одного поля однозначно определяет другое.
  • Partial Dependency (частичная зависимость) — поле зависит только от части составного ключа.
  • Transitive Dependency (транзитивная зависимость) — поле зависит от ключа через другое неключевое поле.
  • Anomaly (аномалия) — ошибка модели при вставке, обновлении или удалении данных.
Вывод: Понимание зависимостей важнее заучивания формулировок нормальных форм.

🗺️ Мини ER-модель (ASCII)

Ниже упрощённый визуальный скелет той же схемы, чтобы быстрее связать 1NF–3NF с реальными таблицами.
🟢 Если совсем просто: Сначала рисуем связи сущностей, потом детализируем поля и ограничения.
🎯 Как понять, что этап прошёл успешно: По схеме понятно, где справочники, где транзакционные таблицы и как они связаны.
cities------id (PK)name   │ city_id (FK)customers---------id (PK)full_namecity_id   │ customer_id (FK)orders------id (PK)customer_idcreated_at   │ order_id (FK)order_items              products----------               --------order_id (PK, FK)        id (PK)product_id (PK, FK) ───► titleqty                      priceunit_price
Вывод: Даже простая ASCII ER-схема снижает риск ошибок в зависимостях перед написанием SQL.

🧱 1. 1NF: атомарность и отсутствие повторяющихся групп

1NF — первый и самый важный барьер от «грязной» структуры данных.
Здесь мы запрещаем хранить список значений в одном поле и приводим запись к виду «одна ячейка = одно значение».
🟢 Если совсем просто: В одной ячейке нельзя хранить сразу несколько значений.
🎯 Как понять, что этап прошёл успешно: Вы не видите полей вроде item_ids = '1,2,3' или phones = '...;...;...'.
Назначение: Убрать повторяющиеся группы и обеспечить атомарность данных.
Простыми словами: Каждое поле хранит только один факт, а не список фактов.
Для новичка: Если вы ставите запятую, чтобы «упаковать» несколько значений в один столбец, это почти всегда нарушение 1NF.
Аналогия: Это как ячейки в Excel: в каждой должна быть одна единица информации, иначе сортировка и фильтрация ломаются.
Пример:
-- Плохо: список товаров в одной строкеCREATE TABLE orders_raw (  order_id BIGINT PRIMARY KEY,  product_ids TEXT,  customer_name TEXT); -- Хорошо: отдельная таблица позиций заказаCREATE TABLE order_items (  order_id BIGINT NOT NULL,  product_id BIGINT NOT NULL,  qty INT NOT NULL CHECK (qty > 0),  PRIMARY KEY (order_id, product_id));
🔎 Как это происходит на практике:
  • Контекст: в исходной таблице есть поля со списками значений.
  • Действия: повторяющиеся группы выносят в отдельные строки/таблицы.
  • Результат: данные становятся пригодными для корректных JOIN, фильтров и агрегаций.
Характеристики:
  • каждое поле атомарно;
  • JSON и массивы допустимы для вспомогательных данных (event payload, metadata), но не для ключевых связей модели;
  • строки легко валидировать и индексировать.
Когда использовать: Всегда, это обязательная база перед 2NF и 3NF.
Вывод: Без 1NF дальнейшая нормализация теряет смысл.

🧩 2. 2NF: убираем частичные зависимости

2NF важна там, где используется составной ключ.
Если неключевое поле зависит только от части такого ключа, оно должно жить в другой таблице.
В типичном OLTP часто используется суррогатный id как PK, поэтому 2NF чаще всего проявляется именно в таблицах-связках, например:
order_items(order_id, product_id).
🟢 Если совсем просто: Если ключ состоит из двух полей, зависимые данные должны зависеть от обоих, а не только от одного.
🎯 Как понять, что этап прошёл успешно: В таблице со составным ключом нет столбцов, которые относятся только к одной стороне связи.
Назначение: Убрать частичные зависимости и сократить дубли справочных данных.
Простыми словами: Информация о товаре должна храниться в таблице товара, а не дублироваться в каждой позиции заказа.
Для новичка: Если поле можно определить, зная только product_id, ему не место в таблице (order_id, product_id).
Аналогия: Это как договор и паспорт: паспортные данные хранятся в карточке человека, а не в каждом договоре с ним.
Пример:
-- Плохо: product_name зависит только от product_idCREATE TABLE order_items_bad (  order_id BIGINT NOT NULL,  product_id BIGINT NOT NULL,  product_name TEXT NOT NULL,  qty INT NOT NULL,  PRIMARY KEY (order_id, product_id)); -- Хорошо: справочные атрибуты товара в productsCREATE TABLE products (  id BIGINT PRIMARY KEY,  product_name TEXT NOT NULL);
🔎 Как это происходит на практике:
  • Контекст: таблица связи содержит много дублирующихся описательных полей.
  • Действия: поля с частичной зависимостью выносят в отдельную сущность.
  • Результат: изменение названия товара делается в одном месте.
Характеристики:
  • применима после 1NF;
  • критична при составных ключах;
  • уменьшает избыточность и update-anomaly.
Когда использовать: Когда в модели есть таблицы вида (A_id, B_id, ...) и справочные поля одной стороны.
Вывод: 2NF отделяет «данные связи» от «данных одной сущности».

🔗 3. 3NF: убираем транзитивные зависимости

Даже после 2NF в таблице могут оставаться поля, которые зависят от ключа не напрямую, а через другое неключевое поле.
Такие зависимости создают «скрытые дубли» и делают обновления опасными.
🟢 Если совсем просто: Неключевые поля не должны зависеть от других неключевых полей.
🎯 Как понять, что этап прошёл успешно: Справочные признаки (например, город клиента) вынесены в отдельные таблицы и не дублируются по каждой строке заказа.
Короткая формула для запоминания: the key, the whole key, and nothing but the key
(неключевые атрибуты должны зависеть от ключа, от полного ключа и ни от чего кроме ключа).
Назначение: Убрать транзитивные зависимости и окончательно стабилизировать модель в пределах 3NF.
Простыми словами: Если customer_id определяет city_id, то city_name не должен лежать в таблице заказа.
Для новичка: Проверьте: можно ли получить значение поля через другое неключевое поле. Если да — вероятно, нужно выносить.
Аналогия: Это как хранить название города в паспорте и в каждом билете человека одновременно — рассинхрон почти гарантирован.
Пример:
-- Плохо: city_name транзитивно зависит от customer_idCREATE TABLE orders_bad (  id BIGINT PRIMARY KEY,  customer_id BIGINT NOT NULL,  city_name TEXT NOT NULL); -- Хорошо: город хранится отдельно и связан ключомCREATE TABLE cities (  id BIGINT PRIMARY KEY,  name TEXT NOT NULL UNIQUE); CREATE TABLE customers (  id BIGINT PRIMARY KEY,  city_id BIGINT NOT NULL REFERENCES cities(id));
🔎 Как это происходит на практике:
  • Контекст: в транзакционных таблицах дублируются справочники.
  • Действия: неключевые справочные атрибуты выносят в отдельные сущности с PK/FK.
  • Результат: изменение справочных значений выполняется централизованно.
Характеристики:
  • применима после 2NF;
  • убирает цепочки зависимостей внутри одной таблицы;
  • снижает риск противоречий в отчётности.
Когда использовать: Когда замечаете дубли справочников и косвенные зависимости между неключевыми полями.
Вывод: 3NF завершает базовую нормализацию и делает схему устойчивой к изменениям.

🧪 4. Пошаговая декомпозиция: от «широкой таблицы» к 3NF

Главный практический навык — не просто знать определения, а уметь разложить реальную таблицу по шагам.
Для джуна это важнее теоретической дискуссии о «дальнейших формах».
🟢 Если совсем просто: Нормализация — это последовательный алгоритм, а не разовая догадка.
🎯 Как понять, что этап прошёл успешно: Вы можете показать схему «до/после» и объяснить, какую аномалию устраняет каждый шаг.
Назначение: Системно преобразовать исходную структуру в 1NF–3NF без потери бизнес-смысла.
Простыми словами: Мы делаем несколько маленьких, проверяемых шагов вместо одной большой «магической» переделки.
Для новичка: Работайте итеративно: сначала 1NF, потом 2NF, потом 3NF, и после каждого шага проверяйте данные.
Аналогия: Это как ремонт квартиры по комнатам: если ломать всё сразу, легко потерять контроль.
Пример:
CREATE TABLE sales_raw (  sale_id BIGINT PRIMARY KEY,  customer_name TEXT,  customer_city TEXT,  product_ids TEXT,  product_names TEXT,  manager_name TEXT);
🔎 Как это происходит на практике:
  • Контекст: есть историческая «широкая» таблица из legacy-системы.
  • Действия: выделяют сущности customers, cities, products, sales, sale_items, задают PK/FK.
  • Результат: схема становится модульной, и каждую сущность можно изменять отдельно.
Характеристики:
  • декомпозиция выполняется по зависимостям, а не по «красоте SQL»;
  • после каждого шага проверяют число строк и консистентность;
  • для миграций готовят сценарий обратной проверки.
Когда использовать: При рефакторинге legacy-таблиц и при проектировании новых доменов на старте.
Вывод: Пошаговая декомпозиция — самый безопасный путь к рабочей 3NF-схеме.

🚀 5. Баланс нормализации и производительности

Частая ошибка — считать, что после нормализации «производительность испортится».
На практике проблема обычно не в нормализации, а в отсутствии индексов и понимания реальных сценариев чтения.
🟢 Если совсем просто: Нормализуем данные, а производительность обеспечиваем индексами и правильными запросами.
🎯 Как понять, что этап прошёл успешно: Ключевые запросы читают данные быстро, а схема остаётся без лишних дублей.
Базовое правило: Индексируйте FK-поля по умолчанию, если таблицы участвуют в регулярных JOIN и фильтрации.
Назначение: Показать, что 3NF и скорость чтения не противоречат друг другу при корректном проектировании.
Простыми словами: Сначала делаем правильную модель данных, потом «подкручиваем» чтение индексами.
Для новичка: Не денормализуйте рано: сначала измерьте реальную проблему через планы выполнения и метрики.
Аналогия: Это как правильная карта города и быстрые дороги: карта отвечает за логику, дороги — за скорость.
Пример:
CREATE INDEX idx_orders_customer_id  ON orders(customer_id); CREATE INDEX idx_order_items_order_id  ON order_items(order_id); SELECT  o.id,  c.full_name,  SUM(oi.qty * oi.price) AS totalFROM orders oJOIN customers c ON c.id = o.customer_idJOIN order_items oi ON oi.order_id = o.idWHERE o.customer_id = 42GROUP BY o.id, c.full_name;
🔎 Как это происходит на практике:
  • Контекст: после нормализации запросы стали более «JOIN-ориентированными».
  • Действия: команда индексирует FK и горячие фильтры, проверяет EXPLAIN ANALYZE.
  • Результат: схема остаётся чистой, а скорость чтения соответствует SLA.
Характеристики:
  • индексы на FK обычно обязательны при росте данных;
  • оптимизация делается по метрикам, а не по ощущениям;
  • денормализация — осознанное исключение, а не стартовая стратегия.
Когда использовать: После приведения модели к 3NF и появления измеримой нагрузки на чтение.
Вывод: Нормализация и производительность совместимы, если проектировать схему под реальные запросы.

⚖️ Сравнение 1NF, 2NF и 3NF

Каждая форма решает свой класс проблем, поэтому важно понимать разницу, а не «перепрыгивать» этапы.
Ниже короткая таблица для быстрого повторения.
🟢 Если совсем просто: 1NF убирает списки в ячейках, 2NF — частичные зависимости, 3NF — транзитивные.
🎯 Как понять, что этап прошёл успешно: Вы можете на живом примере объяснить, какую проблему устраняет каждая форма.
Нормальная формаЧто запрещаетЧто устраняетТипичный результат
1NFсписки/массивы в полечасть insert/update проблематомарные поля
2NFзависимость от части составного ключадубли справочных полей в таблицах-связкахчистые таблицы связей
3NFзависимость неключевых полей друг от другатранзитивные дубли справочниковстабильные справочные связи
Вывод: Последовательный переход 1NF -> 2NF -> 3NF даёт предсказуемую и поддерживаемую модель данных.

📌 Must-know факты

Это минимальный набор, который нужен для практической работы и собеседований по теме нормализации.
Если эти пункты не держать в голове, ошибки в модели почти неизбежны.
🟢 Если совсем просто: Must-know — это «чек-лист выживания» при проектировании таблиц.
🎯 Как понять, что этап прошёл успешно: Вы можете пройтись по своему DDL и подтвердить каждый пункт конкретным примером.
  • 1NF обязательна: никаких списков и повторяющихся групп в одном поле.
  • 2NF актуальна при составных ключах: неключевые атрибуты должны зависеть от полного ключа.
  • 3NF убирает транзитивные зависимости между неключевыми атрибутами.
  • Нормализация решает аномалии insert/update/delete, а не «делает схему красивой».
  • PK/FK/UNIQUE/CHECK закрепляют результат нормализации на уровне БД.
  • После нормализации критичны индексы на FK и частых фильтрах.
  • Денормализация допустима только после измерений и с документированным обоснованием.
Вывод: Если игнорировать must-know по нормализации, данные быстро становятся противоречивыми.

🧨 Частые мифы

Вокруг нормализации много мифов, из-за которых команды либо переусложняют модель, либо отказываются от неё слишком рано.
Ниже самые частые ошибки мышления.
🟢 Если совсем просто: Плохая модель данных почти всегда «начинается с удобного мифа».
🎯 Как понять, что этап прошёл успешно: Вы можете на практике опровергнуть каждый миф и показать рабочую альтернативу.
Миф: Нормализация нужна только для больших корпораций. ✅ Как правильно: Даже маленький проект выигрывает от 1NF–3NF, потому что баги из-за дублей появляются очень рано. 📎 Почему это важно: Чем раньше убрать аномалии, тем меньше переделок в продакшене.
Миф: 3NF всегда делает систему медленной. ✅ Как правильно: Скорость зависит от запросов и индексов; 3NF и производительность совместимы. 📎 Почему это важно: Отказ от нормализации ради «скорости» часто создаёт ещё более дорогие проблемы.
Миф: Если есть ORM, нормализация не нужна. ✅ Как правильно: ORM не отменяет правила хранения данных и не устраняет аномалии модели. 📎 Почему это важно: Неправильная схема остаётся неправильной независимо от фреймворка.
Миф: Денормализация всегда лучше, потому что меньше JOIN. ✅ Как правильно: Денормализация — инструмент оптимизации после измерений, а не базовый дизайн. 📎 Почему это важно: Преждевременная денормализация почти всегда увеличивает число конфликтов данных.

❓ Часто спрашивают на собеседованиях

Эти вопросы почти всегда появляются в блоке SQL/Data Modeling на junior/middle интервью.
Отвечайте через примеры, а не только определениями.
🟢 Если совсем просто: Интервьюер проверяет, умеете ли вы убирать аномалии в реальной схеме.
🎯 Как понять, что этап прошёл успешно: Вы можете объяснить ответы на основе одной предметной области (например, заказы/позиции/клиенты).
Вопрос: Что именно гарантирует 1NF? ✅ Ответ: 1NF гарантирует атомарные поля и отсутствие повторяющихся групп; каждый столбец хранит одно значение на ячейку.
Вопрос: Когда проверка 2NF действительно обязательна? ✅ Ответ: Когда есть составной ключ; нужно убедиться, что неключевые поля зависят от полного ключа, а не от его части.
Вопрос: Как распознать транзитивную зависимость для 3NF? ✅ Ответ: Если неключевое поле можно определить через другое неключевое поле, значит зависимость транзитивная и поле нужно выносить.
Вопрос: Почему нормализация снижает update-anomaly? ✅ Ответ: Потому что каждый факт хранится в одном месте, и его изменение не требует массовых правок дубликатов.
Вопрос: Что делать, если после нормализации запросы стали сложнее? ✅ Ответ: Анализировать реальные запросы, добавлять индексы и оптимизировать JOIN, а не сразу ломать модель денормализацией.
Вопрос: Когда денормализация допустима? ✅ Ответ: Когда есть измеримая проблема производительности, подтверждённая метриками, и есть план поддержания консистентности.
Вопрос: Как объяснить разницу 2NF и 3NF в одной фразе? ✅ Ответ: 2NF убирает зависимости от части составного ключа, 3NF — зависимости неключевых полей друг от друга.
Вопрос: Что важнее при миграции legacy-таблицы: идеальная теория или пошаговый план? ✅ Ответ: Пошаговый план с проверкой данных после каждого шага; иначе риск потери данных и бизнес-остановки слишком высок.

🚫 Типичные ошибки

При нормализации чаще всего ошибаются не в синтаксисе SQL, а в логике зависимостей.
Ниже ошибки, которые стоит проверять на каждом ревью схемы.
🟢 Если совсем просто: Если не проверить зависимости, «формально нормальная» схема всё равно может быть плохой.
🎯 Как понять, что этап прошёл успешно: Вы прошли по каждому пункту и можете объяснить, почему его не нарушаете.

Ошибка 1: считать 1NF «достаточной нормализацией»

Неправильно: Остановиться на атомарных полях и не проверять частичные/транзитивные зависимости.
Правильно: После 1NF обязательно проверить 2NF и 3NF по фактическим зависимостям.
Почему: Основные дубли часто остаются именно после «незавершённой» нормализации.

Ошибка 2: тащить справочные поля в таблицы связей

Неправильно: Хранить product_name или customer_city в таблице позиций заказа.
Правильно: Держать справочные поля в своих сущностях и использовать FK.
Почему: Это классический источник частичных и транзитивных зависимостей.

Ошибка 3: игнорировать insert/delete anomalies

Неправильно: Оценивать модель только по одному SELECT, не проверяя вставку и удаление.
Правильно: Всегда прогонять сценарии insert/update/delete до фиксации схемы.
Почему: Аномалии обычно проявляются именно в операциях записи, а не в простом чтении.

Ошибка 4: преждевременная денормализация «для скорости»

Неправильно: Смешивать сущности до измерений, чтобы «уменьшить количество JOIN».
Правильно: Сначала нормализовать, потом оптимизировать по метрикам и планам запросов.
Почему: Без измерений денормализация чаще создаёт проблемы, чем решает.

Ошибка 5: не документировать зависимости и этапы разбиения

Неправильно: Оставлять схему без объяснения, почему таблица была разделена именно так.
Правильно: Фиксировать в design-notes исходные зависимости, шаги 1NF–3NF и итоговые правила.
Почему: Без документации команда быстро теряет контекст и возвращается к прежним ошибкам.

✅ Best Practices

Ниже практики, которые помогают держать нормализацию «живой» в реальном проекте, а не только в учебном примере.
Используйте их как чек-лист перед каждой миграцией схемы.
🟢 Если совсем просто: Best Practices помогают не откатываться обратно в «широкие» таблицы.
🎯 Как понять, что этап прошёл успешно: Схема проходит ревью и выдерживает типовые сценарии записи/чтения без аномалий.
  • Начинайте с зависимостей данных, а не с готового SQL.
  • Явно отмечайте, какой шаг относится к 1NF, 2NF и 3NF.
  • Любое справочное поле держите в таблице его сущности.
  • Проверяйте аномалии insert/update/delete на тестовых данных.
  • Добавляйте ограничения и индексы сразу после декомпозиции.
  • Индексируйте FK-поля как стандартный шаг после создания связей.
  • Документируйте причины денормализации, если она реально нужна.
  • Делайте миграции небольшими и проверяемыми после каждого шага.

🧾 Заключение

Нормализация 1NF–3NF — это практический инструмент управления качеством данных, а не академическое упражнение.
Если пройти эти шаги последовательно, схема становится устойчивой к росту продукта и изменениям требований.

Ключевые мысли

  • 1NF устраняет неатомарные поля и повторяющиеся группы.
  • 2NF убирает частичные зависимости от составного ключа.
  • 3NF убирает транзитивные зависимости между неключевыми полями.
  • Индексы и ограничения закрепляют результат нормализации в продакшене.
Хорошая нормализация — это когда данные легко менять, сложно сломать и просто объяснить новой команде.
🎯

Проверьте знания

Закрепите материал — пройдите тест по теме «БЛОК 1. Основы реляционных БД — 3. Нормализация 1NF–3NF»

Пройти тест →