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

Что такое ограничения (constraints) в БД?

2.0 Middle🔥 111 комментариев
#DevOps и инфраструктура#Django

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

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

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

Ограничения (constraints) в БД

Constraints (ограничения) — это правила, которые определяют, какие данные могут быть сохранены в таблице БД. Они обеспечивают целостность, непротиворечивость и корректность данных на уровне БД, а не приложения.

Назначение constraints

Вместо того чтобы проверять данные в коде приложения, базы данных позволяют определить правила прямо в схеме. Если кто-то попытается нарушить эти правила — БД просто отклонит операцию. Это защищает от опасных ошибок и несогласованности данных.

Основные типы constraints

PRIMARY KEY — уникальный идентификатор строки:

# SQL
CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(100)
);

# SQLAlchemy
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.orm import declarative_base

Base = declarative_base()

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

Первичный ключ:

  • гарантирует уникальность каждой строки
  • не может быть NULL
  • обычно используется для быстрого поиска и связей

UNIQUE — гарантирует уникальность значения (но может быть NULL):

# SQL
CREATE TABLE users (
    id INT PRIMARY KEY,
    email VARCHAR(100) UNIQUE
);

# SQLAlchemy
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    email = Column(String(100), unique=True)

Отличие от PRIMARY KEY: может быть несколько NULL значений, но только один PRIMARY KEY.

NOT NULL — поле не может быть пустым:

# SQL
CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(100) NOT NULL
);

# SQLAlchemy
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(100), nullable=False)

FOREIGN KEY — связь между таблицами:

# SQL
CREATE TABLE posts (
    id INT PRIMARY KEY,
    user_id INT NOT NULL,
    title VARCHAR(200),
    FOREIGN KEY (user_id) REFERENCES users(id)
);

# SQLAlchemy
class Post(Base):
    __tablename__ = 'posts'
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
    title = Column(String(200))

Основное правило: значение в user_id должно существовать в таблице users. Нельзя создать пост с несуществующим user_id.

CHECK — пользовательское условие:

# SQL
CREATE TABLE products (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    price DECIMAL(10, 2),
    CHECK (price > 0)
);

# SQLAlchemy
from sqlalchemy import CheckConstraint

class Product(Base):
    __tablename__ = 'products'
    id = Column(Integer, primary_key=True)
    name = Column(String(100))
    price = Column(Numeric(10, 2))
    __table_args__ = (CheckConstraint('price > 0'),)

DEFAULT — значение по умолчанию:

# SQL
CREATE TABLE posts (
    id INT PRIMARY KEY,
    title VARCHAR(200),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

# SQLAlchemy
from datetime import datetime

class Post(Base):
    __tablename__ = 'posts'
    id = Column(Integer, primary_key=True)
    title = Column(String(200))
    created_at = Column(DateTime, default=datetime.utcnow)

Каскадные операции (CASCADE)

Что происходит, когда удаляешь пользователя, у которого есть посты?

# SQL
CREATE TABLE posts (
    id INT PRIMARY KEY,
    user_id INT NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

# SQLAlchemy
class Post(Base):
    __tablename__ = 'posts'
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('users.id', ondelete='CASCADE'))

Варианты:

  • CASCADE — удалить все посты при удалении пользователя
  • SET NULL — установить user_id в NULL (если поле nullable)
  • RESTRICT — запретить удаление, если есть зависимые записи
  • NO ACTION — как RESTRICT

Практический пример

from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, CheckConstraint
from sqlalchemy.orm import declarative_base, relationship
from datetime import datetime

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    email = Column(String(100), unique=True, nullable=False)
    age = Column(Integer)
    posts = relationship('Post', back_populates='author', cascade='all, delete-orphan')
    __table_args__ = (CheckConstraint('age >= 0'),)

class Post(Base):
    __tablename__ = 'posts'
    id = Column(Integer, primary_key=True)
    title = Column(String(200), nullable=False)
    content = Column(String(5000))
    user_id = Column(Integer, ForeignKey('users.id', ondelete='CASCADE'), nullable=False)
    created_at = Column(DateTime, default=datetime.utcnow)
    author = relationship('User', back_populates='posts')
    __table_args__ = (CheckConstraint('length(title) > 0'),)

Когда использовать constraints

  • PRIMARY KEY и FOREIGN KEY — всегда, при проектировании схемы
  • NOT NULL — для обязательных полей
  • UNIQUE — для email, username, кода и других уникальных значений
  • CHECK — для диапазонов (возраст > 0, цена >= 0)
  • DEFAULT — для дат создания, статусов по умолчанию

Constraints — это первая линия защиты от ошибок в данных.

Что такое ограничения (constraints) в БД? | PrepBro