← Назад к вопросам
Приведи пример нормализации в виде шаблона
1.0 Junior🔥 231 комментариев
#Python Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Пример нормализации в виде шаблона
Нормализация данных — процесс преобразования данных в стандартный, согласованный формат. Покажу практические примеры как в базах данных, так и в коде Python.
1. Нормализация в реляционной БД
Денормализованная схема (плохо)
-- Проблемы: дублирование, аномалии обновления, избыточность
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_name VARCHAR(100),
customer_email VARCHAR(100),
customer_phone VARCHAR(20),
customer_address VARCHAR(255),
customer_city VARCHAR(50),
product_name VARCHAR(100),
product_price DECIMAL(10,2),
product_category VARCHAR(50),
order_date DATE
);
-- Данные: много дублирования
id | customer_name | customer_email | ... | product_name | product_price
1 | John Doe | john@mail.com | ... | Laptop | 999.99
2 | John Doe | john@mail.com | ... | Mouse | 29.99
-- John Doe и его email повторяются!
Нормализованная схема (хорошо) — 3NF
-- Таблица 1: Клиенты
CREATE TABLE customers (
id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE,
phone VARCHAR(20),
created_at TIMESTAMP
);
-- Таблица 2: Адреса (связь многие-ко-многим)
CREATE TABLE addresses (
id INT PRIMARY KEY,
customer_id INT NOT NULL,
city VARCHAR(50),
street VARCHAR(100),
postal_code VARCHAR(10),
FOREIGN KEY (customer_id) REFERENCES customers(id)
);
-- Таблица 3: Товары
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price DECIMAL(10,2),
category_id INT,
FOREIGN KEY (category_id) REFERENCES categories(id)
);
-- Таблица 4: Заказы
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_id INT NOT NULL,
order_date DATE,
FOREIGN KEY (customer_id) REFERENCES customers(id)
);
-- Таблица 5: Позиции заказа
CREATE TABLE order_items (
id INT PRIMARY KEY,
order_id INT NOT NULL,
product_id INT NOT NULL,
quantity INT,
unit_price DECIMAL(10,2),
FOREIGN KEY (order_id) REFERENCES orders(id),
FOREIGN KEY (product_id) REFERENCES products(id)
);
-- Таблица 6: Категории
CREATE TABLE categories (
id INT PRIMARY KEY,
name VARCHAR(50) UNIQUE
);
-- Теперь данные не дублируются
-- John Doe хранится один раз в customers
-- Его заказы ссылаются на одну запись
2. Нормализация в Python коде
Денормализованная структура (плохо)
from typing import List, Dict
def process_user_data_bad():
"""Данные распределены везде, сложно обновлять"""
user = {
"id": 1,
"name": "John Doe",
"email": "john@mail.com",
"address": {
"street": "123 Main St",
"city": "New York",
"zip": "10001"
},
"orders": [
{
"id": 1,
"user_name": "John Doe", # Дублирование!
"user_email": "john@mail.com", # Дублирование!
"items": [
{
"product_id": 1,
"product_name": "Laptop",
"category_name": "Electronics", # Дублируется
"price": 999.99
}
]
}
]
}
# Проблема: если изменить email, нужно обновить везде
# user["email"] = "newemail@mail.com"
# user["orders"][0]["user_email"] = "newemail@mail.com"
# Может рассинхронизироваться!
return user
Нормализованная структура (хорошо)
from dataclasses import dataclass
from typing import List
from datetime import datetime
@dataclass
class Category:
id: int
name: str
@dataclass
class Product:
id: int
name: str
price: float
category_id: int # Ссылка, не дублирование!
@dataclass
class Address:
id: int
user_id: int
street: str
city: str
zip_code: str
@dataclass
class Customer:
id: int
name: str
email: str
address_id: int # Ссылка
created_at: datetime
@dataclass
class OrderItem:
id: int
order_id: int
product_id: int # Ссылка
quantity: int
unit_price: float
@dataclass
class Order:
id: int
customer_id: int # Ссылка, не дублируем customer_name!
order_date: datetime
items: List[OrderItem] = None # Загружаем отдельно
# Использование
def process_orders_normalized():
# Создаём данные
category = Category(id=1, name="Electronics")
product = Product(id=1, name="Laptop", price=999.99, category_id=category.id)
address = Address(id=1, user_id=1, street="123 Main", city="NYC", zip_code="10001")
customer = Customer(id=1, name="John Doe", email="john@mail.com", address_id=address.id, created_at=datetime.now())
# Если изменить email, достаточно изменить один объект
customer.email = "newemail@mail.com" # Одно место!
return customer, address, product
3. Нормализация данных API
Денормализованный API ответ (плохо)
from fastapi import FastAPI
app = FastAPI()
@app.get("/api/orders/{order_id}")
async def get_order(order_id: int):
"""Ответ с дублированием данных пользователя"""
return {
"order": {
"id": 1,
"date": "2024-03-23",
# Дублирование данных пользователя
"user": {
"id": 1,
"name": "John Doe",
"email": "john@mail.com"
},
"items": [
{
"id": 1,
"product_id": 1,
"product_name": "Laptop",
# Дублирование данных продукта
"product": {
"name": "Laptop",
"price": 999.99,
"category": "Electronics"
}
}
]
}
}
Нормализованный API ответ (хорошо)
from typing import Dict, List
from pydantic import BaseModel
class NormalizedResponse(BaseModel):
"""Нормализованный формат: данные и ID"""
# Основные данные
order: Dict
# Справочники (нормализованные)
users: Dict[int, Dict] # ID -> User
products: Dict[int, Dict] # ID -> Product
categories: Dict[int, Dict] # ID -> Category
@app.get("/api/orders/{order_id}")
async def get_normalized_order(order_id: int):
"""Нормализованный ответ (как Redux/MobX)"""
return {
"order": {
"id": 1,
"date": "2024-03-23",
"user_id": 1, # Только ID
"item_ids": [1] # Только IDs
},
"order_items": {
"1": {
"id": 1,
"order_id": 1,
"product_id": 1,
"quantity": 1
}
},
"users": {
"1": {
"id": 1,
"name": "John Doe",
"email": "john@mail.com"
}
},
"products": {
"1": {
"id": 1,
"name": "Laptop",
"price": 999.99,
"category_id": 1
}
},
"categories": {
"1": {"id": 1, "name": "Electronics"}
}
}
4. Нормализация текстовых данных
import re
from typing import List
def normalize_email(email: str) -> str:
"""Нормализовать email: lowercase, trim"""
return email.strip().lower()
def normalize_phone(phone: str) -> str:
"""Нормализовать телефон: только цифры"""
return re.sub(r'\D', '', phone)
def normalize_text(text: str) -> str:
"""Нормализовать текст"""
# Убрать лишние пробелы
text = ' '.join(text.split())
# Привести к нижнему регистру
text = text.lower()
# Убрать спецсимволы
text = re.sub(r'[^a-z0-9\s]', '', text)
return text
def normalize_address(street: str, city: str, zip_code: str) -> dict:
"""Нормализовать адрес"""
return {
"street": street.strip().title(),
"city": city.strip().upper(),
"zip_code": re.sub(r'\D', '', zip_code)
}
# Примеры
print(normalize_email(" John@MAIL.COM ")) # john@mail.com
print(normalize_phone("+1 (555) 123-4567")) # 15551234567
print(normalize_text(" Hello WORLD!!! ")) # hello world
Ключевые принципы нормализации
# 1. Единая источник истины (Single Source of Truth)
# Данные хранятся в одном месте, ссылаются другие
# 2. Избегать дублирования
# ❌ Плохо: customer_name в orders и customers
# ✅ Хорошо: customer_id в orders, данные в customers
# 3. Минимизировать аномалии обновления
# ❌ Проблема: обновить email надо в 5 местах
# ✅ Решение: обновить в 1 месте
# 4. Облегчить поддержку и расширение
# Новое поле добавляется в одно место
# 5. Оптимизировать для запросов
# Индексы на внешних ключах
# Денормализация ТОЛЬКО при доказанных bottleneck
Вывод
Нормализация — это структурирование данных так, чтобы:
- Не было дублирования
- Каждый факт хранился в одном месте
- Обновление было легко и безопасно
- Связи выражались через ID, не через копирование
Это ключевой принцип проектирования БД (1NF, 2NF, 3NF) и API структур (Redux store, нормализованные API).