← Назад к вопросам

Что такое вторая нормальная форма в БД?

3.0 Senior🔥 11 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое вторая нормальная форма (2NF) в БД?

Вторая нормальная форма (2NF) — это принцип организации данных в реляционной базе данных, который предотвращает частичные зависимости. Другими словами, все неключевые атрибуты должны полностью зависеть от первичного ключа, а не от его части.

Иерархия нормальных форм

1NF (Первая нормальная форма)
   ↓
2NF (Вторая нормальная форма)
   ↓
3NF (Третья нормальная форма)
   ↓
BCNF (Boyce-Codd нормальная форма)

Каждая следующая форма включает требования предыдущих.

Предварительные требования для 2NF

1. Таблица должна быть в 1NF (Первая нормальная форма):

-- ❌ НЕ в 1NF: есть несоставные атрибуты
CREATE TABLE Students (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    skills TEXT  -- "Java, Python, C++" — это список!
);

-- ✅ В 1NF: все атрибуты атомарные (неделимые)
CREATE TABLE Students (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    skill VARCHAR(50)  -- Один скилл на строку
);

Определение 2NF

2NF требует:

  1. Таблица должна быть в 1NF
  2. Все неключевые атрибуты должны полностью зависеть от первичного ключа
  3. Нет частичных зависимостей от составного первичного ключа
Полная зависимость: атрибут зависит от ВСЕХ компонентов составного ключа
Частичная зависимость: атрибут зависит от ЧАСТИ составного ключа ← НАРУШЕНИЕ 2NF

Пример 1: Нарушение 2NF (Плохо)

-- ❌ НЕ в 2NF: есть частичная зависимость
CREATE TABLE StudentCourses (
    student_id INT,
    course_id INT,
    student_name VARCHAR(100),  -- Зависит ТОЛЬКО от student_id!
    course_name VARCHAR(100),   -- Зависит ТОЛЬКО от course_id!
    grade CHAR(1),              -- Зависит от ОБОИХ (student_id, course_id)
    PRIMARY KEY (student_id, course_id)
);

-- Данные:
-- student_id | course_id | student_name | course_name | grade
--     1      |     101   |   "Alice"    | "Java"      |  A
--     1      |     102   |   "Alice"    | "Python"    |  B
--     2      |     101   |   "Bob"      | "Java"      |  C

-- Проблема:
-- - student_name зависит только от student_id (ЧАСТИЧНАЯ зависимость)
-- - course_name зависит только от course_id (ЧАСТИЧНАЯ зависимость)
-- Это нарушение 2NF!

Пример 2: Исправление в 2NF (Хорошо)

-- ✅ В 2NF: разделили на три таблицы

-- Таблица студентов
CREATE TABLE Students (
    student_id INT PRIMARY KEY,
    student_name VARCHAR(100)
);

-- Таблица курсов
CREATE TABLE Courses (
    course_id INT PRIMARY KEY,
    course_name VARCHAR(100)
);

-- Таблица связей (студент-курс)
CREATE TABLE StudentCourses (
    student_id INT,
    course_id INT,
    grade CHAR(1),
    PRIMARY KEY (student_id, course_id),
    FOREIGN KEY (student_id) REFERENCES Students(student_id),
    FOREIGN KEY (course_id) REFERENCES Courses(course_id)
);

-- Теперь:
-- - StudentCourses таблица содержит ТОЛЬКО данные, зависящие от обоих ключей
-- - grade зависит от ОБОИХ (student_id И course_id) ✅
-- - Нет частичных зависимостей ✅

Пример 3: Заказ с товарами (Нарушение 2NF)

-- ❌ НЕ в 2NF
CREATE TABLE OrderItems (
    order_id INT,
    product_id INT,
    product_name VARCHAR(100),     -- Зависит только от product_id!
    product_price DECIMAL(10, 2),  -- Зависит только от product_id!
    quantity INT,                  -- Зависит от обоих
    total DECIMAL(10, 2),          -- Зависит от обоих
    PRIMARY KEY (order_id, product_id)
);

-- Данные:
-- order_id | product_id | product_name | product_price | quantity | total
--    1     |    101     | "Ноутбук"    |    1000.00   |    1     | 1000.00
--    1     |    102     | "Мышка"      |     20.00    |    2     |  40.00
--    2     |    101     | "Ноутбук"    |    1000.00   |    3     | 3000.00

-- Проблемы:
-- 1. product_name повторяется (аномалия обновления)
-- 2. Если удалить заказ 1, потеряем информацию о товаре
-- 3. Нарушена целостность данных

Пример 4: Исправление в 2NF (Правильно)

-- ✅ В 2NF

CREATE TABLE Products (
    product_id INT PRIMARY KEY,
    product_name VARCHAR(100),
    product_price DECIMAL(10, 2)
);

CREATE TABLE Orders (
    order_id INT PRIMARY KEY,
    order_date DATE,
    customer_id INT
);

CREATE TABLE OrderItems (
    order_id INT,
    product_id INT,
    quantity INT,
    total DECIMAL(10, 2),
    PRIMARY KEY (order_id, product_id),
    FOREIGN KEY (order_id) REFERENCES Orders(order_id),
    FOREIGN KEY (product_id) REFERENCES Products(product_id)
);

-- Теперь:
-- - Каждый факт хранится в одном месте
-- - Нет дублирования
-- - Нет частичных зависимостей

Пример 5: Сотрудники и Отделения (Нарушение)

-- ❌ НЕ в 2NF
CREATE TABLE Employees (
    employee_id INT,
    project_id INT,
    employee_name VARCHAR(100),     -- Зависит только от employee_id!
    department_name VARCHAR(100),   -- Зависит только от employee_id!
    salary DECIMAL(10, 2),          -- Зависит только от employee_id!
    project_hours INT,              -- Зависит от ОБОИХ
    PRIMARY KEY (employee_id, project_id)
);

-- Данные:
-- employee_id | project_id | employee_name | department_name | salary | project_hours
--      1      |    101     |  "Alice"      |  "IT"           | 5000   |     40
--      1      |    102     |  "Alice"      |  "IT"           | 5000   |     30
--      2      |    101     |  "Bob"        |  "HR"           | 4000   |     20

-- Проблемы - дублирование данных!

Пример 6: Исправление в 2NF

-- ✅ В 2NF

CREATE TABLE Employees (
    employee_id INT PRIMARY KEY,
    employee_name VARCHAR(100),
    department_id INT,
    salary DECIMAL(10, 2),
    FOREIGN KEY (department_id) REFERENCES Departments(department_id)
);

CREATE TABLE Departments (
    department_id INT PRIMARY KEY,
    department_name VARCHAR(100)
);

CREATE TABLE EmployeeProjects (
    employee_id INT,
    project_id INT,
    project_hours INT,
    PRIMARY KEY (employee_id, project_id),
    FOREIGN KEY (employee_id) REFERENCES Employees(employee_id),
    FOREIGN KEY (project_id) REFERENCES Projects(project_id)
);

CREATE TABLE Projects (
    project_id INT PRIMARY KEY,
    project_name VARCHAR(100)
);

Симптомы нарушения 2NF

Признаки того, что таблица НЕ в 2NF:

  1. Дублирование данных

    -- Одна информация о товаре повторяется в разных строках
    
    order_id | product_id | product_name | product_price | quantity
       1     |    101     |  "Ноутбук"   |    1000.00   |    1
       1     |    102     |  "Мышка"     |     20.00    |    2
       2     |    101     |  "Ноутбук"   |    1000.00   |    1  ← Дублирование!
    
  2. Аномалия обновления (Update Anomaly)

    • Если обновить price для product_id=101 в одной строке, нужно обновить во всех
  3. Аномалия удаления (Deletion Anomaly)

    • Если удалить последний заказ товара, потеряем информацию о товаре
  4. Аномалия вставки (Insertion Anomaly)

    • Нельзя добавить новый товар, если он не связан с заказом

Таблица сравнения: 1NF vs 2NF

Критерий1NF2NF
Атомарные значения✅ Да✅ Да
Первичный ключ✅ Есть✅ Есть
Полная зависимость❌ Нет✅ Да
Частичные зависимости❌ Могут быть✅ Нет
Дублирование❌ Возможно✅ Минимально

Практический алгоритм проверки 2NF

1. Проверь, есть ли в таблице составной первичный ключ (несколько полей)
   Если НЕТ → таблица может быть в 2NF
   Если ДА → перейди к шагу 2

2. Для каждого неключевого атрибута проверь:
   Зависит ли он от ВСЕХ полей первичного ключа?
   
   Если ДА → нормально
   Если НЕТ (зависит только от части) → НАРУШЕНИЕ 2NF!

3. Если нашёл нарушение → разделить таблицу

Когда применяется 2NF?

Используй 2NF для:

  • Любой реляционной БД (SQL Server, PostgreSQL, MySQL)
  • Систем с составными первичными ключами
  • Когда много таблиц со связями many-to-many

⚠️ Возможные исключения:

  • NoSQL системы (не реляционные)
  • Денормализация для производительности (OLAP системы)
  • Специальные случаи в data warehouse

Взаимосвязь: 1NF → 2NF → 3NF

1NF: Атомарные значения
  ↓
2NF: Нет частичных зависимостей (+ требования 1NF)
  ↓
3NF: Нет транзитивных зависимостей (+ требования 2NF)

Best Practices

Правильно:

  • Выделяй в отдельные таблицы данные, зависящие от части ключа
  • Используй foreign keys для связи таблиц
  • Думай о составном ключе: от каких частей ключа зависит каждый атрибут?
  • Проверяй на дублирование данных

Неправильно:

  • Не помещай в таблицу данные, зависящие только от части ключа
  • Не дублируй информацию в разных строках
  • Не игнорируй признаки нарушения (дублирование, аномалии)

Вывод

Вторая нормальная форма (2NF) требует, чтобы:

  1. Таблица была в 1NF (атомарные значения)
  2. Все неключевые атрибуты полностью зависели от первичного ключа (а не от его части)
  3. Нет частичных зависимостей

Практический результат:

  • Нет дублирования данных
  • Нет аномалий обновления, удаления и вставки
  • Данные легче обновлять и поддерживать
  • Целостность БД гарантирована

Простое правило: Если у тебя составной ключ (например, student_id + course_id), убедись, что каждый неключевой атрибут зависит от ОБОИХ полей ключа, а не только от одного.

Что такое вторая нормальная форма в БД? | PrepBro