Читаем вместе. PostgreSQL 16 изнутри. Стр. 45 — 49

Егор Рогов. Postgres 15 изнутриПродолжаю читать книгу «PostgreSQL Изнутри».

Начинаю Часть I «Изоляция и многоверсионность».

Глава 2 «Изоляция».

На этот раз вообще всего-лишь один небольшой раздел:

  • 2.1. Согласованность

В этом разделе описаны два важных понятия:

  • Согласованность (consistency)
  • Ограничения целостности (integrity constraints)
  • Согласованность строже чем целостность
  • Приложение может нарушить согласованность, не нарушая целостности, у СУБД нет способа узнать об этом.

Но это не значит что можно забить на целостность. Тут я сразу отправлю в пару источников. Например, можно почитать SQL стандарт. В стандарте 2023 года есть раздел 4.25 Integrity constraints. Там много и подробно и на английском языке.

Но можно сделать проще — открыть книгу Евгений Павловича Моргунова PostgreSQL. Основы языка SQL, и там — раздел 5.1. Значения по умолчанию и ограничения целостности. Там понятно много расписано. Я ниже только основную информацию оттуда покажу, но со своими примерами:

  • Значения по умолчанию DEFAULT
  • Ограничение CHECK
  • Ограничение NOT NULL
  • Ограничение уникальности UNIQUE
  • Первичный ключ PRIMARY KEY
  • Внешний ключ FOREIGN KEY

Почти все эти ограничения можно описать двумя способами — либо на уровне столбца, либо на уровне таблицы. Еще можно указывать названия ограничений, можно не указывать.

На уровне столбцов:

CREATE TABLE for_constraints (id integer CONSTRAINT pk_for_constraints PRIMARY KEY,
	mark numeric (1) CONSTRAINT def_fc DEFAULT 5,
	CONSTRAINT check_mark CHECK (mark >= 3 AND mark <= 5),
	book_num numeric (5) CONSTRAINT unique_book_num UNIQUE NOT NULL
);

На уровне таблицы:

CREATE TABLE for_constraints2 (id integer,
	mark numeric(1) DEFAULT 5,
	book_num numeric (5) NOT NULL,
	CONSTRAINT pk_for_constraints2 PRIMARY KEY (id),
	CONSTRAINT check_mark2 CHECK (mark >= 3 AND mark <= 5),
	CONSTRAINT unique_book_num2 UNIQUE (book_num)
);

Внешний ключ можно описать вот так:

CREATE TABLE for_fk (id_fk integer REFERENCES for_constraints (id));

Либо так:

CREATE TABLE for_fk2 (id_fk integer,
	CONSTRAINT fk_for_constraints2 FOREIGN KEY (id_fk) REFERENCES for_constraints2
);

А вот и пример. Я туда добавил разные ошибки — на первичный ключ, на дублирование, чтобы сразу было видно какие сообщения генерируются:

-- Сразу подготовка - посмотрим ограничения и индексы с помощью подготовленного оператора, понадобится чуть позже

PREPARE constrs (text) as
SELECT pgc.conname AS constraint_name,
       ccu.column_name,
           CASE WHEN contype = 'c' THEN 'check'
                        WHEN contype = 'f' THEN 'foreign key'
                        WHEN contype = 'p' THEN 'primary key'
                        WHEN contype = 'u' THEN 'unique'
                        WHEN contype = 't' THEN 'trigger'
                        WHEN contype = 'x' THEN 'exclusion'
           END constraint,
       pg_get_constraintdef(pgc.oid)
FROM pg_constraint pgc
         JOIN pg_namespace nsp ON nsp.oid = pgc.connamespace
         JOIN pg_class  cls ON pgc.conrelid = cls.oid
         left JOIN information_schema.constraint_column_usage ccu
                   ON pgc.conname = ccu.constraint_name
                       AND nsp.nspname = ccu.constraint_schema
WHERE table_name = $1
union all
SELECT 'null_not_null' constraint_name, attname column_name, 
	CASE WHEN attnotnull = 't' THEN 'not null' ELSE 'null' END constraint, 
	CASE WHEN attnotnull = 't' THEN 'NOT NULL' ELSE 'NULL' END pg_get_constraintdef 
FROM pg_attribute 
  WHERE attrelid = $1::regclass 
  AND attname NOT IN ('tableoid', 'cmax', 'cmin', 'xmax','xmin', 'ctid');

-- Ограничения на уровне столбцов
CREATE TABLE for_constraints (id integer CONSTRAINT pk_for_constraints PRIMARY KEY,
							  mark numeric (1) CONSTRAINT def_fc DEFAULT 5,
							  CONSTRAINT check_mark CHECK (mark >= 3 AND mark <= 5),
							  book_num numeric (5) CONSTRAINT unique_book_num UNIQUE NOT NULL
							 );
-- PRIMARY KEY
INSERT INTO for_constraints VALUES (1,5,1);
INSERT INTO for_constraints VALUES (1,5,2);
-- DEFAULT

SELECT * FROM for_constraints;
-- CHECK
INSERT INTO for_constraints VALUES (2, 4, 2);
INSERT INTO for_constraints VALUES (3, 8, 3);
-- UNIQUE
INSERT INTO for_constraints VALUES (4, 4, 2);
-- FOREIGN KEY
CREATE TABLE for_fk (id_fk integer REFERENCES for_constraints (id));
INSERT INTO for_fk VALUES (1);
INSERT INTO for_fk VALUES (11);
-- Посмотрим ограничения и индексы
EXECUTE constrs ('for_constraints');

-- Другой вариант - ограничения на уровне таблицы
CREATE TABLE for_constraints2 (id integer,
							   mark numeric(1) DEFAULT 5,
							   book_num numeric (5) NOT NULL,
							   CONSTRAINT pk_for_constraints2 PRIMARY KEY (id),
							   CONSTRAINT check_mark2 CHECK (mark >= 3 AND mark <= 5),
							   CONSTRAINT unique_book_num2 UNIQUE (book_num)
							  );
-- PRIMARY KEY
INSERT INTO for_constraints2 VALUES (1,5,1);
INSERT INTO for_constraints2 VALUES (1,5,2);
-- DEFAULT
INSERT INTO for_constraints2 (id, book_num) VALUES (1,1);
SELECT * FROM for_constraints2;
-- CHECK
INSERT INTO for_constraints2 VALUES (2, 4, 2);
INSERT INTO for_constraints2 VALUES (3, 8, 3);
-- UNIQUE
INSERT INTO for_constraints2 VALUES (4, 4, 2);
-- FOREIGN KEY
CREATE TABLE for_fk2 (id_fk integer,
					  CONSTRAINT fk_for_constraints2 FOREIGN KEY (id_fk) REFERENCES for_constraints2
					 );
INSERT INTO for_fk2 VALUES (1);
INSERT INTO for_fk2 VALUES (11);
-- Посмотрим ограничения и индексы
EXECUTE constrs ('for_constraints2');


-- Очистка лишнего
DROP TABLE for_constraints CASCADE;
DROP TABLE for_constraints2 CASCADE;
DROP TABLE for_fk CASCADE;
DROP TABLE for_fk2 CASCADE;
DEALLOCATE constrs;

[свернуть]

Ну и немного исследования исходного кода:

И про транзакции. Вспоминая студенческие времена — тогда я долго не мог понять что такое транзакция, даже читая учебник. Но ведь вроде всё понятно и логично написано. Так что просто продублирую книгу:

Таким образом, транзакцией называется множество операций, которые переводят базу данных из одного корректного состояния в другое корректное состояние (согласованность) при условии, что транзакция выполнена полностью (атомарность) и без помех со стороны других транзакций (изоляция). Это определение объединяет требования, стоящие за первыми тремя буквами акронима ACID: Atomicity, Consistency, Isolation.

Для начала можно запомнить что транзакция либо выполняется полностью, либо не выполняется ни один из операторов, входящих в неё.

Транзакция открывается словом BEGIN, заверить её нужно COMMIT, ROLLBACK либо END. Но помните, что в программах, с помощью которых вы работаете с PostgreSQL может быть активировать режим автокоммита.

BEGIN;
CREATE TABLE t_tmp (id integer);
SELECT * FROM pg_tables where tablename = 't_tmp';
ROLLBACK;
SELECT * FROM pg_tables where tablename = 't_tmp';

Про разные виды транзакций далее будет рассказано подробнее.


Be the first to comment

Leave a Reply

Ваш Mail не будет опубликован.


*