Индекс не может быть отложен - не имеет значения, является ли он UNIQUE
частичным или нет, только UNIQUE
ограничением. Другие типы ограничений ( FOREIGN KEY
, PRIMARY KEY
, EXCLUDE
) также откладываемые - но не CHECK
ограничение.
Таким образом, уникальный частичный индекс (и неявное ограничение, которое он реализует) будет проверяться при каждом утверждении (и фактически после каждой вставки / обновления строки в текущей реализации), а не в конце транзакции.
Если вы хотите реализовать это ограничение как отложенное, вы можете добавить еще одну таблицу в проект. Что-то вроде этого:
CREATE TABLE public.booking_status
( booking_id int NOT NULL, -- same types
check_in timestamp NOT NULL, -- as in
check_out timestamp NOT NULL, -- booking
CONSTRAINT unique_booking
UNIQUE (check_in, check_out)
DEFERRABLE INITIALLY DEFERRED,
CONSTRAINT unique_booking_fk
FOREIGN KEY (booking_id, check_in, check_out)
REFERENCES public.booking (booking_id, check_in, check_out)
DEFERRABLE INITIALLY DEFERRED
) ;
С этим дизайном и предполагая, что у booking_status
него есть только 2 возможных варианта (0 и 1), вы можете полностью удалить его из booking
(если есть строка в booking_status
, это 1, если не 0).
Другим способом было бы (ab) использовать EXCLUDE
ограничение:
ALTER TABLE booking
ADD CONSTRAINT unique_booking
EXCLUDE
( check_in WITH =,
check_out WITH =,
(CASE WHEN booking_status = 1 THEN TRUE END) WITH =
)
DEFERRABLE INITIALLY DEFERRED ;
Проверено на dbfiddle .
Что выше делает:
CASE
Выражение становится , NULL
когда booking_status
равна нулю или отличается от 1. Мы могли бы написать(CASE WHEN booking_status = 1 THEN TRUE END)
, как (booking_status = 1 OR NULL)
если это делает любой более ясным.
Уникальные и исключающие ограничения принимают строки, в которых одно или несколько выражений равно NULL. Таким образом, он действует как отфильтрованный индекс сWHERE booking_status = 1
.
Все WITH
операторы=
так действуют как UNIQUE
ограничение.
Эти два вместе делают ограничение действовать как отфильтрованный уникальный индекс.
Но это ограничение, и EXCLUDE
ограничения могут быть отложены.
(CASE WHEN booking_status = 1 THEN TRUE END) WITH =)
должно быть заменено на,) WHERE (booking_status = 1)
потому что «Ограничения исключения реализованы с использованием индекса», и этот частичный индексWHERE
будет меньше и быстрее - postgresql.org/docs/current/sql-createtable.html и postgresql.org/docs/current/sql- createindex.htmlХотя годы этого вопроса прошли, я хотел бы уточнить для испаноязычных, тесты были проведены в Postgres:
Следующее ограничение было добавлено в таблицу из 1337 записей, где комплект является первичным ключом:
Это создает первичный ключ по умолчанию NOT DEFERRED для таблицы, поэтому при попытке следующего UPDATE мы получаем ошибку:
В Postgres выполнение UPDATE для каждого ROW проверяет, удовлетворено ли ОГРАНИЧЕНИЕ или ОГРАНИЧЕНИЕ.
CONSTRAINT IMMEDIATE теперь создан, и каждый оператор выполняется отдельно:
Здесь SI позволяет изменить первичный ключ, поскольку он выполняет все первое полное предложение (1328 строк); но хотя он находится в транзакции (BEGIN), CONSTRAINT проверяется сразу после завершения каждого предложения без выполнения COMMIT, поэтому выдает ошибку при выполнении INSERT. Наконец, мы создали CONSTRAINT DEFERRED и сделаем следующее:
Если мы выполним каждый оператор ** Block 2 **, каждое предложение отдельно, в INSERT не будет сгенерировано никакой ошибки, поскольку он не проверяется, но выполняется последний COMMIT, где он обнаруживает несоответствие.
Для полной информации на английском я предлагаю вам проверить ссылки:
Отложенные ограничения SQL в глубине
НЕ ОТДЫХАЕТСЯ, а НЕ ОТДЫХАЕТСЯ ПЕРВОНАЧАЛЬНО НЕМЕДЛЕННО
источник