Además de lo que proporcionó @Craig (y corrigiendo algunos de ellos):
Postgres efectiva 9.4 , UNIQUE
, PRIMARY KEY
y EXCLUDE
las restricciones sean comprobadas inmediatamente después de cada fila cuando se define NOT DEFERRABLE
. Esto es diferente de otros tipos de NOT DEFERRABLE
restricciones (actualmente solo REFERENCES
(clave externa)) que se verifican después de cada declaración . Trabajamos todo esto bajo esta pregunta relacionada sobre SO:
Es no suficiente para una UNIQUE
(o PRIMARY KEY
o EXCLUDE
) la restricción sea DEFERRABLE
para hacer que su código presentado con varias instrucciones de trabajo.
Y usted puede no utilizar ALTER TABLE ... ALTER CONSTRAINT
para este propósito. Por documentación:
ALTER CONSTRAINT
Este formulario altera los atributos de una restricción que se creó previamente. Actualmente solo se pueden alterar las restricciones de clave externa .
El énfasis audaz es mío. Use en su lugar:
ALTER TABLE t
DROP CONSTRAINT category_name_key
, ADD CONSTRAINT category_name_key UNIQUE(name) DEFERRABLE;
Descarte y agregue la restricción de nuevo en una sola declaración para que no haya una ventana de tiempo para que nadie pueda colarse en las filas ofensivas. Para tablas grandes sería tentador conservar el índice único subyacente de alguna manera, porque es costoso eliminarlo y recrearlo. Por desgracia, eso no parece posible con las herramientas estándar (si tiene una solución para eso, ¡háganoslo saber!):
Para una sola declaración, hacer que la restricción sea diferible es 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;
Una consulta con CTE también es una declaración única :
WITH x AS (
UPDATE category SET name = 'phones' WHERE id = 1
)
UPDATE category SET name = 'tablets' WHERE id = 2;
Sin embargo , para su código con múltiples declaraciones, usted (adicionalmente) necesita diferir la restricción, o definirla como INITIALLY DEFERRED
O bien, generalmente es más costoso que el anterior. Pero puede no ser fácilmente factible agrupar todo en una sola declaración.
BEGIN;
SET CONSTRAINTS category_name_key DEFERRED;
UPDATE category SET name = 'phones' WHERE id = 1;
UPDATE category SET name = 'tablets' WHERE id = 2;
COMMIT;
Sin embargo, tenga en cuenta una limitación en relación con las FOREIGN KEY
restricciones. Por documentación:
Las columnas a las que se hace referencia deben ser las columnas de una restricción de clave primaria o única no diferible en la tabla de referencia.
Entonces no puedes tener ambos al mismo tiempo.