Além do que o @Craig forneceu (e corrigiu alguns deles ):
Eficaz Postgres 9.4 , UNIQUE
, PRIMARY KEY
e EXCLUDE
as restrições são verificadas imediatamente após cada linha quando definido NOT DEFERRABLE
. Isso é diferente de outros tipos de NOT DEFERRABLE
restrições (atualmente apenas REFERENCES
(chave estrangeira)) que são verificadas após cada instrução . Nós resolvemos tudo isso sob esta pergunta relacionada no SO:
É não o suficiente para uma UNIQUE
(ou PRIMARY KEY
ou EXCLUDE
restrição) para ser DEFERRABLE
fazer o seu código apresentado com várias instruções de trabalho.
E você não pode usar ALTER TABLE ... ALTER CONSTRAINT
para esse fim. Por documentação:
ALTER CONSTRAINT
Este formulário altera os atributos de uma restrição criada anteriormente. Atualmente, apenas as restrições de chave estrangeira podem ser alteradas .
Negrito ênfase minha. Use em vez disso:
ALTER TABLE t
DROP CONSTRAINT category_name_key
, ADD CONSTRAINT category_name_key UNIQUE(name) DEFERRABLE;
Solte e adicione a restrição de volta em uma única instrução, para que não haja janela de tempo para alguém se infiltrar nas linhas ofensivas. Para tabelas grandes, seria tentador conservar o índice exclusivo subjacente de alguma forma, porque é caro excluí-lo e recriá-lo. Infelizmente, isso não parece possível com ferramentas padrão (se você tiver uma solução para isso, informe-nos!):
Para uma única declaração, tornar a restrição adiada é suficiente:
UPDATE category c
SET name = c_old.name
FROM category c_old
WHERE c.id IN (1,2)
AND c_old.id IN (1,2)
AND c.id <> c_old.id;
Uma consulta com CTEs também é uma única instrução:
WITH x AS (
UPDATE category SET name = 'phones' WHERE id = 1
)
UPDATE category SET name = 'tablets' WHERE id = 2;
No entanto , para o seu código com várias instruções, você (adicionalmente) precisa realmente adiar a restrição - ou defini-la como INITIALLY DEFERRED
Ou normalmente é mais cara que a anterior. Mas pode não ser facilmente viável agrupar tudo em uma única declaração.
BEGIN;
SET CONSTRAINTS category_name_key DEFERRED;
UPDATE category SET name = 'phones' WHERE id = 1;
UPDATE category SET name = 'tablets' WHERE id = 2;
COMMIT;
Esteja ciente de uma limitação em conexão com FOREIGN KEY
restrições, no entanto. Por documentação:
As colunas referenciadas devem ser as colunas de uma restrição de chave primária ou única não diferida na tabela referenciada.
Então você não pode ter os dois ao mesmo tempo.