Ответ
Нормализация — это процесс проектирования схемы реляционной базы данных для минимизации избыточности данных и аномалий при вставке, обновлении или удалении. Основная цель — обеспечение целостности данных.
Основные нормальные формы:
- 1NF (Первая нормальная форма): Устранить повторяющиеся группы. Все значения в столбцах атомарны (неделимы), каждая запись уникальна.
- 2NF (Вторая нормальная форма): Должна выполняться 1NF. Устранить частичные зависимости — все неключевые атрибуты должны полностью зависеть от всего первичного ключа (а не от его части).
- 3NF (Третья нормальная форма): Должна выполняться 2NF. Устранить транзитивные зависимости — неключевые атрибуты не должны зависеть от других неключевых атрибутов.
Пример на Node.js с PostgreSQL:
-- Денормализованная таблица (проблемы: дублирование, аномалии обновления)
CREATE TABLE denormalized_orders (
order_id INT,
customer_name VARCHAR,
customer_email VARCHAR,
product_name VARCHAR,
product_price DECIMAL
);
-- Нормализованная схема (3NF)
CREATE TABLE customers (
customer_id SERIAL PRIMARY KEY,
name VARCHAR NOT NULL,
email VARCHAR UNIQUE NOT NULL
);
CREATE TABLE products (
product_id SERIAL PRIMARY KEY,
name VARCHAR NOT NULL,
price DECIMAL NOT NULL
);
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(customer_id) ON DELETE CASCADE,
order_date TIMESTAMP DEFAULT NOW()
);
CREATE TABLE order_items (
order_id INT REFERENCES orders(order_id) ON DELETE CASCADE,
product_id INT REFERENCES products(product_id),
quantity INT NOT NULL,
PRIMARY KEY (order_id, product_id)
);
Практическое применение в Node.js: При работе с ORM, например, Sequelize, нормализация отражается в моделях и связях:
// Модели Sequelize, соответствующие нормализованной схеме
const Customer = sequelize.define('Customer', { name: DataTypes.STRING, email: DataTypes.STRING });
const Product = sequelize.define('Product', { name: DataTypes.STRING, price: DataTypes.DECIMAL });
const Order = sequelize.define('Order', { date: DataTypes.DATE });
const OrderItem = sequelize.define('OrderItem', { quantity: DataTypes.INTEGER });
// Определение связей
Customer.hasMany(Order);
Order.belongsTo(Customer);
Order.belongsToMany(Product, { through: OrderItem });
Product.belongsToMany(Order, { through: OrderItem });
Преимущества: Целостность данных, эффективное использование памяти, упрощение операций обновления. Недостаток: Усложнение запросов из-за необходимости частых JOIN, что может влиять на производительность для сложных выборок.
Ответ 18+ 🔞
А, нормализация, ёпта! Ну это когда ты пытаешься навести порядок в своей базе данных, чтобы она не превратилась в такой же пиздец, как твоя квартира после трёхдневной пьянки. Представь, что у тебя одна таблица, куда ты скидываешь всё подряд: и кто заказ сделал, и что купил, и почту клиента, и цену товара. Через месяц ты сам от себя охуеешь, когда поймёшь, что почту клиента надо поменять в овердохуища записей, а товар переименовали — и ты опять в пизде.
Так вот, чтобы этого не было, умные дядьки придумали нормальные формы. Это как инструкция по сборке шкафа из Икеи, только если её не соблюдать, у тебя не просто дверца отвалится, а вся твоя CRM-ка накроется медным тазом.
Вот эти самые формы, коротко, чтобы не бздеть:
- Первая нормальная форма (1NF): Это базовый уровень, чувак. Прекрати пихать в одну ячейку кучу данных через запятую, как список покупок. Каждое значение — отдельно, каждая запись — уникальна. Никаких массивов в столбцах, ядрёна вошь!
- Вторая нормальная форма (2NF): Тут уже интереснее. Допустим, у тебя ключ составной (заказ + товар). И у товара есть цена. Так вот, цена должна зависеть от всего ключа (от пары заказ-товар)? Нет, блядь, она зависит только от товара! Это частичная зависимость, от неё надо избавляться. Выноси товар с его ценой в отдельную таблицу, не будь распиздяем.
- Третья нормальная форма (3NF): А это уже высший пилотаж. Смотри: у тебя в заказе есть ID клиента, а у клиента — его менеджер. Получается, менеджер зависит от заказа через клиента? Это транзитивная зависимость, хуй с горы. Выноси клиентов с их менеджерами в свою таблицу. Правило простое: неключевые поля должны зависеть только от первичного ключа, а не друг от друга. Иначе волнение ебать — обновил менеджера у клиента в одном месте, а в старых заказах он старый и остался. Пизда рулю.
Пример, как было и как стало:
Было — бардак полный, одна таблица на весь мир:
CREATE TABLE denormalized_orders (
order_id INT,
customer_name VARCHAR, -- А если имя изменится? Обновлять все заказы? Да похуй, правда?
customer_email VARCHAR, -- То же самое. Доверия ебать ноль к таким данным.
product_name VARCHAR, -- Переименовали товар? Удачи.
product_price DECIMAL
);
Стало — порядок, красота, 3NF:
-- Клиенты отдельно
CREATE TABLE customers (
customer_id SERIAL PRIMARY KEY,
name VARCHAR NOT NULL,
email VARCHAR UNIQUE NOT NULL -- Один раз поменял и везде ок.
);
-- Товары отдельно
CREATE TABLE products (
product_id SERIAL PRIMARY KEY,
name VARCHAR NOT NULL,
price DECIMAL NOT NULL
);
-- Заказы (только факт заказа и кто)
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(customer_id) ON DELETE CASCADE,
order_date TIMESTAMP DEFAULT NOW()
);
-- А что именно в заказе — в отдельной связующей таблице
CREATE TABLE order_items (
order_id INT REFERENCES orders(order_id) ON DELETE CASCADE,
product_id INT REFERENCES products(product_id),
quantity INT NOT NULL,
PRIMARY KEY (order_id, product_id)
);
Как это в ноде крутить, если ты на ORM типа Sequelize:
// Описываешь модели, которые отражают твой новый, чистый мир.
const Customer = sequelize.define('Customer', { name: DataTypes.STRING, email: DataTypes.STRING });
const Product = sequelize.define('Product', { name: DataTypes.STRING, price: DataTypes.DECIMAL });
const Order = sequelize.define('Order', { date: DataTypes.DATE });
const OrderItem = sequelize.define('OrderItem', { quantity: DataTypes.INTEGER });
// И говоришь им, как они друг с другом связаны. Это магия, бля.
Customer.hasMany(Order);
Order.belongsTo(Customer);
Order.belongsToMany(Product, { through: OrderItem }); // Вот эта связь "многие-ко-многим" через OrderItem — это и есть суть нормализации.
Product.belongsToMany(Order, { through: OrderItem });
Итог, ёпта:
- Плюсы: Данные целые, не дублируются, обновлять что-то одно в одном месте — просто песня. Памяти жрёт меньше.
- Минус: Чтобы собрать полную информацию о заказе, тебе теперь надо склеить (
JOIN) кучу таблиц. Если делать это бездумно и часто, можно так нагрузить базу, что она тебе скажет «иди ты нахуй» и будет права. Но это уже вопрос оптимизации запросов и индексов, другая история.
Видео-ответы
▶
▶
▶
▶
▶
▶