Что такое нормализация базы данных?

Ответ

Нормализация — это процесс проектирования структуры реляционной базы данных, направленный на устранение избыточности данных и аномалий при операциях вставки, обновления и удаления (INSERT, UPDATE, DELETE). Она достигается путем декомпозиции таблиц и приведения их к нормальным формам.

Основные нормальные формы, которые я применяю на практике:

  1. Первая нормальная форма (1NF): Устраняет повторяющиеся группы. Каждое поле содержит атомарные (неделимые) значения, а в таблице нет массивов или списков в одной ячейке.

    • До: Orders(order_id, products: "Книга,Ручка,Блокнот")
    • После: Отдельная таблица Order_Items(order_id, product_name).
  2. Вторая нормальная форма (2NF): Должна выполняться 1NF, и все неключевые атрибуты должны полностью зависеть от всего составного первичного ключа, а не от его части.

    • Проблема: В таблице Order_Items(order_id, product_id, product_name, quantity) поле product_name зависит только от product_id, а не от пары (order_id, product_id).
    • Решение: Вынести product_name в отдельную таблицу Products(product_id, product_name).
  3. Третья нормальная форма (3NF): Должна выполняться 2NF, и не должно быть транзитивных зависимостей (когда неключевой атрибут зависит от другого неключевого атрибута).

    • Проблема: В таблице Orders(order_id, customer_id, customer_phone) поле customer_phone зависит от customer_id, а не напрямую от order_id.
    • Решение: Вынести customer_phone в таблицу Customers(customer_id, customer_phone).

Пример нормализованной схемы для заказов:

-- Основные сущности
CREATE TABLE customers (
    customer_id INT PRIMARY KEY,
    name VARCHAR(100),
    email VARCHAR(100) UNIQUE
);

CREATE TABLE products (
    product_id INT PRIMARY KEY,
    name VARCHAR(100),
    price DECIMAL(10,2)
);

-- Связующие таблицы
CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    order_date DATE,
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);

CREATE TABLE order_items (
    order_id INT,
    product_id INT,
    quantity INT,
    PRIMARY KEY (order_id, product_id),
    FOREIGN KEY (order_id) REFERENCES orders(order_id),
    FOREIGN KEY (product_id) REFERENCES products(product_id)
);

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

Ответ 18+ 🔞

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

Если по-простому, то нормализация — это когда ты наводишь порядок в своих таблицах, чтобы данные не дублировались как у дурака, и чтобы когда ты что-то обновлял, удалял или вставлял, у тебя всё не ебнулось к чертям. Аномалии эти самые — они как хитрая жопа: вроде всё работает, а потом раз — и пиздец.

Вот как я это обычно делаю, на пальцах:

  1. Первая нормальная форма (1NF): Это базовый уровень, ёпта. Ты должен избавиться от всякой хуйни вроде списков через запятую в одной ячейке. Каждое значение — атомарное, то есть неделимое. Представь, что у тебя в поле products записано "Молоток,Гвозди,Доска". Это пиздец, а не поле. Как ты потом искать будешь? Что обновлять?

    • Было: Заказы(id_заказа, товары: "Молоток,Гвозди,Доска") — манда с ушами.
    • Стало: Отдельная таблица ПозицииЗаказа(id_заказа, название_товара). Всё, теперь каждый товар — отдельная запись. Порядок.
  2. Вторая нормальная форма (2NF): Тут уже интереснее. Чтоб её достичь, нужна первая. Суть в том, что если у тебя ключ составной (из нескольких полей), то все остальные поля должны зависеть от ВСЕГО этого ключа, а не от его кусочка. Иначе будет пиздопроебибна.

    • Проблема: Допустим, таблица ПозицииЗаказа(id_заказа, id_товара, название_товара, количество). Ключ у нас (id_заказа, id_товара). Но поле название_товара зависит только от id_товара! Получается, если один и тот же товар в 100 заказах, его название будет повторяться 100 раз. Обновил название в одном месте — и сиди, обновляй в остальных 99. Доверия к такой схеме — ноль ебать.
    • Решение: Выносим название_товара в отдельную таблицу Товары(id_товара, название_товара). Теперь всё чисто.
  3. Третья нормальная форма (3NF): Это когда уже и вторая есть. Тут ловим транзитивные зависимости. Звучит страшно, а на деле: неключевое поле не должно зависеть от другого неключевого поля. Оно должно зависеть ТОЛЬКО от первичного ключа.

    • Проблема: Таблица Заказы(id_заказа, id_клиента, телефон_клиента). Ключ — id_заказа. Поле телефон_клиента зависит не от ключа, а от id_клиента. Опять дублирование: один клиент сделал 50 заказов — его телефон записан 50 раз. Клиент сменил номер — волнение ебать, надо бегать по всем заказам.
    • Решение: Выносим телефон_клиента (и вообще все данные клиента) в таблицу Клиенты(id_клиента, телефон_клиента). Теперь логично, да?

Вот смотри, как выглядит нормальная схема после всех этих телодвижений:

-- Основные сущности (клиенты и товары — отдельно, как и положено)
CREATE TABLE customers (
    customer_id INT PRIMARY KEY,
    name VARCHAR(100),
    email VARCHAR(100) UNIQUE
);

CREATE TABLE products (
    product_id INT PRIMARY KEY,
    name VARCHAR(100),
    price DECIMAL(10,2)
);

-- Заказы — это одна сущность
CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    order_date DATE,
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);

-- А что внутри заказа — это связующая таблица. Классика!
CREATE TABLE order_items (
    order_id INT,
    product_id INT,
    quantity INT,
    PRIMARY KEY (order_id, product_id),
    FOREIGN KEY (order_id) REFERENCES orders(order_id),
    FOREIGN KEY (product_id) REFERENCES products(product_id)
);

Что в итоге? Преимущества — да, ёбать колотить, целостность данных, обновлять что-то в одном месте — красота, место экономится. Но есть и подстава: чтобы получить полную информацию о заказе, тебе теперь надо сделать JOIN аж по трём таблицам. На маленьких объёмах — похуй, а когда данных овердохуища, запросы могут тормозить. Поэтому для чисто аналитических или высоконагруженных на чтение систем иногда идут на денормализацию — сознательно допускают дублирование, чтобы меньше джойнить. Но это уже осознанный грех, а не беспорядок по незнанию. Сначала научись нормализовать, а потом уже решай, где можно нахулиганить.