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

Каким образом изменяется информация в реляционных БД?

1.0 Junior🔥 231 комментариев
#Базы данных (SQL)

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

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

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

Изменение информации в реляционных БД: CRUD операции

CRUD (Create, Read, Update, Delete) — это четыре основные операции для работы с данными в реляционных базах данных. Каждая операция соответствует SQL команде и подчиняется принципам ACID.

1. CREATE: Вставка данных (INSERT)

Добавление новых записей в таблицу.

-- Базовый INSERT
INSERT INTO users (id, name, email) 
VALUES (1, 'Alice', 'alice@example.com');

-- Множественная вставка
INSERT INTO users (id, name, email) VALUES
(2, 'Bob', 'bob@example.com'),
(3, 'Charlie', 'charlie@example.com');

-- С автоинкрементом
INSERT INTO users (name, email) 
VALUES ('Dave', 'dave@example.com');
-- id будет сгенерирован автоматически

-- Вставка из другой таблицы
INSERT INTO users_archived (name, email)
SELECT name, email FROM users WHERE created_at < '2020-01-01';

На Python:

from sqlalchemy import text
from sqlalchemy.orm import Session

# Способ 1: Raw SQL
db.execute(text(
    "INSERT INTO users (name, email) VALUES (:name, :email)"
), {"name": "Alice", "email": "alice@example.com"})
db.commit()

# Способ 2: ORM (SQLAlchemy)
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    name = Column(String)
    email = Column(String)

def create_user(db: Session, name: str, email: str):
    user = User(name=name, email=email)
    db.add(user)
    db.commit()
    db.refresh(user)  # получить id
    return user

user = create_user(db, "Alice", "alice@example.com")
print(user.id)  # будет выведен присвоенный id

# Способ 3: Batch insert (для производительности)
def bulk_insert_users(db: Session, users_data):
    users = [User(name=u['name'], email=u['email']) for u in users_data]
    db.add_all(users)  # добавить все сразу
    db.commit()

bulk_insert_users(db, [
    {'name': 'Bob', 'email': 'bob@example.com'},
    {'name': 'Charlie', 'email': 'charlie@example.com'}
])

2. READ: Чтение данных (SELECT)

Получение данных из таблицы с фильтрацией и сортировкой.

-- Получить все столбцы
SELECT * FROM users;

-- Получить конкретные столбцы
SELECT name, email FROM users;

-- С условием WHERE
SELECT * FROM users WHERE email = 'alice@example.com';

-- С сортировкой
SELECT * FROM users ORDER BY name ASC;

-- С агрегацией
SELECT 
    COUNT(*) as total_users,
    COUNT(DISTINCT email) as unique_emails
FROM users;

-- С JOIN
SELECT u.name, o.order_id, o.amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.id = 1;

На Python:

from sqlalchemy import select, func

# Способ 1: Raw SQL
result = db.execute(text(
    "SELECT * FROM users WHERE email = :email"
), {"email": "alice@example.com"})
user = result.fetchone()

# Способ 2: ORM query
def get_user_by_email(db: Session, email: str):
    return db.query(User).filter(User.email == email).first()

user = get_user_by_email(db, "alice@example.com")
print(user.name)  # Alice

# Способ 3: Modern ORM (SQLAlchemy 2.0+)
def get_all_users(db: Session):
    stmt = select(User).order_by(User.name)
    return db.execute(stmt).scalars().all()

# Способ 4: С фильтрацией
def get_users_by_domain(db: Session, domain: str):
    stmt = select(User).where(User.email.endswith(domain))
    return db.execute(stmt).scalars().all()

users = get_users_by_domain(db, "example.com")

# Способ 5: Агрегация
def get_user_count(db: Session):
    return db.query(func.count(User.id)).scalar()

count = get_user_count(db)  # 3

# Способ 6: JOIN
def get_user_with_orders(db: Session, user_id: int):
    user = db.query(User).filter(User.id == user_id).first()
    orders = db.query(Order).filter(Order.user_id == user_id).all()
    return {"user": user, "orders": orders}

3. UPDATE: Обновление данных (UPDATE)

Изменение существующих записей.

-- Обновить один столбец
UPDATE users SET email = 'newemail@example.com' WHERE id = 1;

-- Обновить несколько столбцов
UPDATE users SET email = 'new@example.com', updated_at = NOW() 
WHERE name = 'Alice';

-- Обновить с вычислением
UPDATE orders SET total_price = quantity * unit_price 
WHERE status = 'pending';

-- Обновить с JOIN (PostgreSQL)
UPDATE orders o
SET status = 'shipped'
FROM users u
WHERE o.user_id = u.id AND u.is_premium = TRUE;

На Python:

from sqlalchemy import update

# Способ 1: ORM
def update_user_email(db: Session, user_id: int, new_email: str):
    user = db.query(User).filter(User.id == user_id).first()
    if user:
        user.email = new_email
        db.commit()
        db.refresh(user)
        return user
    return None

user = update_user_email(db, 1, "newemail@example.com")

# Способ 2: Bulk update (эффективнее)
def bulk_update_status(db: Session, user_ids: list, status: str):
    stmt = update(User).where(
        User.id.in_(user_ids)
    ).values(status=status)
    db.execute(stmt)
    db.commit()

# Способ 3: Raw SQL
db.execute(text(
    "UPDATE users SET email = :email WHERE id = :id"
), {"email": "new@example.com", "id": 1})
db.commit()

# Способ 4: Conditional update
def increment_view_count(db: Session, post_id: int):
    stmt = update(Post).where(
        Post.id == post_id
    ).values(view_count=Post.view_count + 1)
    db.execute(stmt)
    db.commit()

4. DELETE: Удаление данных (DELETE)

Удаление записей из таблицы.

-- Удалить конкретную запись
DELETE FROM users WHERE id = 1;

-- Удалить с условием
DELETE FROM logs WHERE created_at < '2020-01-01';

-- Удалить все (осторожно!)
DELETE FROM users;  -- удаляет ВСЕ

-- Удалить с JOIN (каскадное удаление вручную)
DELETE FROM orders
WHERE user_id IN (
    SELECT id FROM users WHERE is_banned = TRUE
);

На Python:

from sqlalchemy import delete

# Способ 1: ORM
def delete_user(db: Session, user_id: int):
    user = db.query(User).filter(User.id == user_id).first()
    if user:
        db.delete(user)
        db.commit()
        return True
    return False

# Способ 2: Bulk delete
def delete_old_logs(db: Session, days: int):
    from datetime import datetime, timedelta
    
    cutoff_date = datetime.now() - timedelta(days=days)
    stmt = delete(Log).where(Log.created_at < cutoff_date)
    db.execute(stmt)
    db.commit()

# Способ 3: Raw SQL
db.execute(text(
    "DELETE FROM users WHERE id = :id"
), {"id": 1})
db.commit()

# Способ 4: Каскадное удаление через отношения
class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    name = Column(String)
    # Каскадное удаление: удаляет все заказы при удалении пользователя
    orders = relationship("Order", cascade="all, delete-orphan")

class Order(Base):
    __tablename__ = "orders"
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey("users.id"))

# При удалении пользователя все его заказы тоже удаляются
user = db.query(User).get(1)
db.delete(user)
db.commit()  # заказы тоже удаляются

ACID свойства при изменении данных

Atomicity (Атомарность): Операция либо полностью выполняется, либо откатывается.

def transfer_money(db: Session, from_user: int, to_user: int, amount: float):
    try:
        # Снять со счета
        db.execute(text(
            "UPDATE accounts SET balance = balance - :amount WHERE user_id = :user_id"
        ), {"amount": amount, "user_id": from_user})
        
        # Пополнить счет
        db.execute(text(
            "UPDATE accounts SET balance = balance + :amount WHERE user_id = :user_id"
        ), {"amount": amount, "user_id": to_user})
        
        db.commit()  # обе операции выполнены
    except Exception as e:
        db.rollback()  # откатить обе операции
        raise e

Consistency (Консистентность): БД остается в корректном состоянии.

# Constraints и triggers гарантируют консистентность
class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    email = Column(String, unique=True)  # UNIQUE constraint
    age = Column(Integer, CheckConstraint('age >= 18'))  # CHECK
    created_at = Column(DateTime, default=datetime.now)

# Попытка добавить дубликат email
try:
    db.add(User(email="alice@example.com"))
    db.add(User(email="alice@example.com"))  # ошибка!
    db.commit()
except IntegrityError:
    db.rollback()

Isolation (Изоляция): Параллельные транзакции не влияют друг на друга.

# Уровни изоляции
db.execute(text("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;"))
db.execute(text("SET TRANSACTION ISOLATION LEVEL READ COMMITTED;"))
db.execute(text("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;"))
db.execute(text("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;"))

Durability (Устойчивость): Коммитнутые данные сохраняются даже при сбое.

db.commit()  # данные записаны на диск
# Даже если сервер выключится, данные сохранятся

Транзакции для複ных операций

from sqlalchemy import begin

def create_order_with_items(db: Session, user_id: int, items: list):
    with db.begin():  # открыть транзакцию
        # Создать заказ
        order = Order(user_id=user_id, total=0)
        db.add(order)
        db.flush()  # flush чтобы получить id
        
        total = 0
        # Добавить товары
        for item in items:
            order_item = OrderItem(
                order_id=order.id,
                product_id=item['product_id'],
                quantity=item['quantity']
            )
            db.add(order_item)
            total += item['price'] * item['quantity']
        
        # Обновить сумму
        order.total = total
        db.commit()  # все или ничего

try:
    create_order_with_items(db, 1, [
        {'product_id': 1, 'quantity': 2, 'price': 10.0},
        {'product_id': 2, 'quantity': 1, 'price': 20.0}
    ])
except Exception as e:
    print(f"Order creation failed: {e}")
    # Все операции откачены

Optimistic vs Pessimistic Locking

# Pessimistic: блокируем строку
def update_with_lock(db: Session, user_id: int):
    user = db.query(User).with_for_update().filter(
        User.id == user_id
    ).first()  # другие транзакции ждут
    
    # Безопасно обновляем
    user.balance -= 100
    db.commit()

# Optimistic: проверяем версию
class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    balance = Column(Float)
    version = Column(Integer, default=0)  # версия

def update_optimistic(db: Session, user_id: int, expected_version: int):
    user = db.query(User).filter(User.id == user_id).first()
    
    if user.version != expected_version:
        raise ValueError("Data was modified by another process")
    
    user.balance -= 100
    user.version += 1  # инкремент версии
    db.commit()

Сравнение операций

ОперацияКомандаСкоростьРискованность
CreateINSERTбыстронизкий
ReadSELECTбыстронет риска
UpdateUPDATEсреднеесредний
DeleteDELETEбыстроВЫСОКИЙ

Best Practices

# 1. Всегда используй WHERE для UPDATE и DELETE
db.execute(text("UPDATE users SET ... WHERE id = :id"))  # ✓
# db.execute(text("UPDATE users SET ..."))  # ✗ опасно!

# 2. Используй транзакции для сложных операций
with db.begin():
    # несколько операций
    db.add(...)
    db.delete(...)

# 3. Проверяй результаты
stmt = update(User).where(User.id == 1).values(name="New")
result = db.execute(stmt)
print(f"Updated {result.rowcount} rows")

# 4. Используй ORM вместо raw SQL (когда возможно)
# Защита от SQL injection, type safety

# 5. Добавляй индексы на часто используемые WHERE колонки
class User(Base):
    __tablename__ = "users"
    email = Column(String, index=True)