Ограничения целостности

Constraint заставляет базу проверять инвариант независимо от того, какой экземпляр приложения или скрипт изменяет данные.

CREATE TABLE orders (
    id         BIGINT PRIMARY KEY,
    user_id    BIGINT NOT NULL,
    state      VARCHAR(20) NOT NULL,
    total      NUMERIC(12, 2) NOT NULL,
    created_at TIMESTAMP NOT NULL,
 
    CONSTRAINT fk_orders_user
        FOREIGN KEY (user_id) REFERENCES users(id),
    CONSTRAINT ck_orders_total
        CHECK (total >= 0),
    CONSTRAINT ck_orders_state
        CHECK (state IN ('NEW', 'PAID', 'CANCELLED'))
);

Основные ограничения

NOT NULL

Значение обязательно. Пустая строка остается строкой и не нарушает NOT NULL.

UNIQUE

Запрещает повтор комбинации столбцов. Поведение нескольких NULL в unique constraint зависит от стандарта, СУБД и дополнительных опций — важную семантику задают явно.

UNIQUE (order_id, sku)

PRIMARY KEY

Главный ключ строки: уникален и не допускает NULL. Один constraint может быть составным.

FOREIGN KEY

Проверяет существование родительского ключа. Политика удаления должна соответствовать домену:

  • RESTRICT/NO ACTION — запретить удаление при ссылках;
  • CASCADE — удалить зависимые строки;
  • SET NULL — убрать ссылку, если столбец допускает NULL.

Cascade удобен, но способен удалить большой граф данных; это часть модели, а не техническая мелочь.

CHECK

Проверяет условие для строки. В SQL CHECK обычно отклоняет FALSE, но пропускает UNKNOWN, поэтому обязательный столбец требует отдельного NOT NULL.

quantity INTEGER NOT NULL CHECK (quantity > 0)

Когда constraint сложен

Правило между множеством строк — например «не больше одной активной записи» — иногда выражается частичным unique index. Правило между сервисами обычный constraint одной БД не охватит.

Изменение существующей таблицы

Перед добавлением constraint нужны:

  1. поиск нарушающих строк;
  2. очистка/backfill;
  3. оценка блокировки и времени проверки;
  4. безопасное развертывание приложения;
  5. мониторинг нарушения после включения.

Связано: Ключи БД, Транзакции принцип ACID, Нормализация денормализация.

Вопросы

Чем NOT NULL отличается от CHECK? NOT NULL явно запрещает отсутствие значения; обычный CHECK может пропустить UNKNOWN.

Зачем constraint, если проверяет Java-код? Базу меняют несколько процессов, версии приложения и скрипты; constraint остается общей последней защитой.

Создает ли foreign key индекс на дочернем столбце? Не обязательно.

Когда опасен CASCADE? Когда удаление родителя запускает неожиданно большой или критичный каскад.

Можно ли constraint защитить правило между двумя независимыми БД? Нет, нужен распределенный протокол и модель согласованности.