Раздел 33 · PostgreSQL с нуля

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

junior~20 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

В прошлом уроке NOT NULL не пустил в таблицу книгу без названия. Это первое ограничение, но не единственное. Цена не бывает отрицательной, ISBN не повторяется, у каждой книги свой номер. Такие правила можно записать прямо в таблицу, и тогда база будет проверять каждую вставку и каждое изменение сама.

Охранник на входе

На входе в бизнес-центр стоит охранник со списком правил: пропуск обязателен, по одному пропуску проходит один человек, детей до 14 лет без взрослых не пускать. Охраннику всё равно, кто тебя прислал и как ты торопишься. Нарушил правило, значит не прошёл.

Ограничение целостности это такой охранник при таблице. Приложение может ошибиться, программист может забыть проверку, кто-то полезет в базу руками через GUI. Ограничение сработает в любом случае, потому что стоит на последнем рубеже, у самих данных.

Теорию ограничений мы разбирали в уроках про ключи и целостность. Здесь смотрим, как они пишутся в PostgreSQL и что он говорит, когда правило нарушено.

Четыре вида

Таблица книг с полным набором охраны:

CREATE TABLE books (
  id    integer       PRIMARY KEY,
  isbn  text          NOT NULL UNIQUE,
  title text          NOT NULL,
  price numeric(8, 2) CHECK (price > 0),
  pages integer       NOT NULL CHECK (pages > 0)
);

INSERT INTO books VALUES (1, '978-5-389-01686-6', 'Мастер и Маргарита', 690.00, 480);
ОграничениеПравилоГде в таблице
NOT NULLзначение обязательноtitle, isbn, pages
UNIQUEзначение не повторяетсяisbn
PRIMARY KEYне повторяется и не пустоеid
CHECKвыражение истинноprice, pages

NOT NULL мы уже знаем. Нарушим по очереди остальные три.

PRIMARY KEY

Первичный ключ это номер строки, по которому её всегда можно найти. Вставим вторую книгу с тем же id:

INSERT INTO books VALUES (1, '978-5-17-090334-4', 'Пикник на обочине', 450.00, 224);
ERROR:  duplicate key value violates unique constraint "books_pkey"
DETAIL:  Key (id)=(1) already exists.

Первичный ключ это UNIQUE и NOT NULL вместе. Ключ у таблицы может быть только один, и его стоит заводить в каждой таблице, даже если кажется, что он не нужен.

UNIQUE

UNIQUE запрещает повторы, но первичным ключом не считается. Подходит для альтернативных ключей: у книги есть id, но и ISBN двух книг совпадать не может.

INSERT INTO books VALUES (2, '978-5-389-01686-6', 'Пикник на обочине', 450.00, 224);
ERROR:  duplicate key value violates unique constraint "books_isbn_key"
DETAIL:  Key (isbn)=(978-5-389-01686-6) already exists.

Сообщение почти такое же, как у ключа: под капотом оба работают через уникальный индекс.

CHECK

CHECK принимает любое логическое выражение. Отрицательная цена:

INSERT INTO books VALUES (2, '978-5-17-090334-4', 'Пикник на обочине', -450.00, 224);
ERROR:  new row for relation "books" violates check constraint "books_price_check"
DETAIL:  Failing row contains (2, 978-5-17-090334-4, Пикник на обочине, -450.00, 224).

А теперь без цены вовсе:

INSERT INTO books VALUES (2, '978-5-17-090334-4', 'Пикник на обочине', NULL, 224);
SELECT id, title, price FROM books ORDER BY id;
 id |       title        | price
----+--------------------+--------
  1 | Мастер и Маргарита | 690.00
  2 | Пикник на обочине  |
(2 rows)

Строка прошла. Это не ошибка: NULL > 0 даёт не ложь, а «неизвестно», трёхзначная логика в действии. CHECK отклоняет только явную ложь. Если цена обязательна, нужен ещё и NOT NULL, как у pages.

урок из курса

Урок из курса «PostgreSQL с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.

Оплата пока не подключена: премиум и курсы открываются промокодом.