Что такое ограничения (constraints) в БД?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Ограничения (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 — это первая линия защиты от ошибок в данных.