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

Заставляем базу самостоятельно отвергать неверные данные с помощью ограничений.

Ограничение (constraint) — это правило на уровне таблицы, которое PostgreSQL проверяет при каждой вставке и обновлении, отклоняя данные, его нарушающие.

Зачем нужны ограничения

Можно проверять данные в коде приложения — но приложений может быть несколько, и каждое способно ошибиться. Ограничения переносят проверку в саму базу: что бы ни случилось, в таблицу не попадёт строка без обязательного поля, с отрицательной ценой или с дублирующимся email. Это последний и самый надёжный рубеж защиты.

Пять основных ограничений

ОграничениеЧто гарантирует
PRIMARY KEYуникальный и непустой идентификатор строки
UNIQUEзначение не повторяется в столбце
NOT NULLзначение обязательно (не может быть пустым)
CHECKзначение удовлетворяет условию
DEFAULTзначение по умолчанию, если не задано

Пример с проверкой в песочнице

Соберём таблицу сотрудников с несколькими ограничениями и проверим, что корректные данные проходят. Пример переносимый.

CREATE TABLE employees (
    id     INTEGER PRIMARY KEY,
    email  TEXT NOT NULL UNIQUE,
    salary INTEGER CHECK (salary > 0)
);

INSERT INTO employees (id, email, salary) VALUES
    (1, '[email protected]', 90000),
    (2, '[email protected]', 75000);

SELECT email, salary FROM employees ORDER BY salary DESC;

Вывод:

[email protected]|90000
[email protected]|75000

Здесь email обязателен (NOT NULL) и уникален (UNIQUE), а зарплата обязана быть положительной (CHECK). Попытка вставить второй такой же email или зарплату -100 завершилась бы ошибкой — база не пропустит.

CHECK — проверки на уровне таблицы

CHECK — самое гибкое ограничение: внутри можно записать почти любое логическое условие, в том числе с несколькими столбцами.

CREATE TABLE bookings (
    id          SERIAL PRIMARY KEY,
    starts_at   DATE NOT NULL,
    ends_at     DATE NOT NULL,
    guests      INTEGER NOT NULL,
    -- условие с двумя столбцами:
    CHECK (ends_at > starts_at),
    CHECK (guests BETWEEN 1 AND 10)
);

Первое CHECK гарантирует, что дата выезда позже даты заезда, второе — что гостей от 1 до 10. Такие правила невозможно «забыть» применить — они часть таблицы.

Именованные ограничения

Ограничению можно дать имя через CONSTRAINT. Тогда в сообщении об ошибке будет понятное название, и ограничение легко удалить позже.

CREATE TABLE products (
    id    SERIAL PRIMARY KEY,
    sku   TEXT,
    price NUMERIC(10, 2),
    CONSTRAINT uq_sku UNIQUE (sku),
    CONSTRAINT positive_price CHECK (price >= 0)
);

Итог

  • Ограничения переносят проверку данных в базу — это самый надёжный рубеж целостности.
  • PRIMARY KEY = уникальный + непустой; UNIQUE и NOT NULL по отдельности тоже доступны.
  • CHECK задаёт произвольное условие, в том числе по нескольким столбцам; ограничениям можно давать имена через CONSTRAINT.
Проверьте себя
1. Что гарантирует ограничение PRIMARY KEY?
AЧто значение положительное
BЧто значение одновременно уникально и не равно NULL
CЧто значение по умолчанию равно 0
DЧто столбец текстовый
2. Какое ограничение позволит задать условие salary > 0?
AUNIQUE
BNOT NULL
CCHECK
DDEFAULT
3. Главное преимущество ограничений в базе перед проверками в коде приложения?
AОни работают быстрее запросов
BИх соблюдает сама база при любых вставках, какое бы приложение ни писало данные
CОни уменьшают размер таблицы
DОни заменяют индексы
Поддержать проект