Um índice não pode ser adiado - não importa se é UNIQUE
ou não, parcial ou não, apenas uma UNIQUE
restrição. Outros tipos de restrições ( FOREIGN KEY
, PRIMARY KEY
, EXCLUDE
) também estão deferrable - mas não CHECK
restrições.
Portanto, o índice parcial exclusivo (e a restrição implícita que ele implementa) serão verificados em todas as instruções (e, de fato, após cada inserção / atualização de linha na implementação atual), não no final da transação.
O que você pode fazer, se desejar implementar essa restrição como adiada, é adicionar mais uma tabela no design. Algo assim:
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
) ;
Com esse design e assumindo que booking_status
só booking
existem 2 opções possíveis (0 e 1), você pode removê-lo totalmente de (se houver uma linha em booking_status
, é 1, se não for 0).
Outra maneira seria (ab) usar uma EXCLUDE
restrição:
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 ;
Testado no dbfiddle .
O que o acima faz:
A CASE
expressão se torna NULL
quando booking_status
é nula ou diferente de 1. Poderíamos escrever (CASE WHEN booking_status = 1 THEN TRUE END)
como (booking_status = 1 OR NULL)
se isso tornasse mais claro.
As restrições exclusivas e de exclusão aceitam linhas em que uma ou mais das expressões é NULL. Por isso, atua como um índice filtrado com WHERE booking_status = 1
.
Todos os WITH
operadores são =
assim que age como uma UNIQUE
restrição.
Esses dois combinados fazem a restrição atuar como um índice exclusivo filtrado.
Mas é uma restrição e as EXCLUDE
restrições podem ser adiadas.
(CASE WHEN booking_status = 1 THEN TRUE END) WITH =)
deve ser substituído) WHERE (booking_status = 1)
por "As restrições de exclusão são implementadas usando um índice", e esse índice parcialWHERE
será menor e mais rápido - postgresql.org/docs/current/sql-createtable.html e postgresql.org/docs/current/sql- createindex.htmlEmbora os anos dessa pergunta tenham passado, gostaria de esclarecer para os falantes de espanhol, os testes foram feitos no Postgres:
A seguinte restrição foi adicionada a uma tabela de 1337 registros, em que o kit é a chave primária:
Isso cria uma chave primária padrão NÃO DEFERIDA para a tabela, portanto, ao tentar a próxima ATUALIZAÇÃO, obtemos um erro:
No Postgres, a execução de uma atualização para cada linha verifica se a restrição ou restrição é atendida.
O CONSTRAINT IMMEDIATE agora é criado e cada instrução é executada separadamente:
Aqui, o SI permite alterar a chave primária, uma vez que executa toda a primeira frase completa (1328 linhas); mas, embora esteja em transação (BEGIN), o CONSTRAINT é validado imediatamente após o término de cada sentença sem ter feito COMMIT, portanto, gera o erro ao executar o INSERT. Por fim, criamos o CONSTRAINT DEFERRED, faça o seguinte:
Se executarmos cada instrução do ** Bloco 2 **, cada sentença separadamente, nenhum erro será gerado para o INSERT, pois ele não valida, mas o COMMIT final é executado onde encontra uma inconsistência.
Para informações completas em inglês, sugiro que você verifique os links:
Restrições SQL adiadas em profundidade
NÃO DEFERRABLE versus DEFERRABLE INICIALMENTE IMEDIATO
fonte