Un índice no puede diferirse, no importa si es UNIQUE
o no, parcial o no, solo una UNIQUE
restricción. Otros tipos de restricciones ( FOREIGN KEY
, PRIMARY KEY
, EXCLUDE
) también son diferible - pero no CHECK
limitaciones.
Por lo tanto, el índice parcial único (y la restricción implícita que implementa) se verificará en cada declaración (y de hecho después de cada inserción / actualización de fila en la implementación actual), no al final de la transacción.
Lo que podría hacer, si desea implementar esta restricción como diferible, es agregar una tabla más en el diseño. Algo como esto:
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
) ;
Con este diseño y suponiendo que booking_status
solo tiene 2 opciones posibles (0 y 1), puede eliminarlo por completo booking
(si hay una fila en booking_status
, es 1, si no es 0).
Otra forma sería (ab) usar una EXCLUDE
restricción:
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 ;
Probado en dbfiddle .
Lo que hace lo anterior:
La CASE
expresión se convierte NULL
cuando booking_status
es nulo o diferente a 1. Podríamos escribir (CASE WHEN booking_status = 1 THEN TRUE END)
como (booking_status = 1 OR NULL)
si eso lo aclarara más.
Las restricciones exclusivas y de exclusión aceptan filas donde una o más de las expresiones son NULL. Por lo tanto, actúa como un índice filtrado con WHERE booking_status = 1
.
Todos los WITH
operadores son =
así que actúa como una UNIQUE
restricción.
Estos dos combinados hacen que la restricción actúe como un índice único filtrado.
Pero es una restricción y las EXCLUDE
restricciones pueden diferirse.
(CASE WHEN booking_status = 1 THEN TRUE END) WITH =)
debe reemplazarse) WHERE (booking_status = 1)
por "las restricciones de exclusión se implementan mediante un índice", y este índice parcialWHERE
será más pequeño y más rápido: postgresql.org/docs/current/sql-createtable.html y postgresql.org/docs/current/sql- createindex.htmlAunque han pasado los años de esta pregunta, me gustaría aclarar para los hispanohablantes, las pruebas se han realizado en Postgres:
La siguiente restricción se agregó a una tabla de 1337 registros, donde el kit es la clave principal:
Esto crea una clave primaria predeterminada NO DIFERIDA para la tabla, por lo que al intentar la próxima ACTUALIZACIÓN obtenemos un error:
En Postgres, la ejecución de una ACTUALIZACIÓN para cada FILA verifica que se cumple la RESTRICCIÓN o RESTRICCIÓN.
El CONSTRAINT INMEDIATE ahora se crea y cada instrucción se ejecuta por separado:
Aquí SI permite cambiar la clave primaria ya que ejecuta la primera oración completa completa (1328 filas); pero aunque está en la transacción (BEGIN), la CONSTRAINT se valida inmediatamente después de terminar cada oración sin haber hecho COMMIT, por lo tanto genera el error al ejecutar el INSERT. Finalmente creamos la CONSTRAINT DEFERRED para hacer lo siguiente:
Si ejecutamos cada declaración del ** Bloque 2 **, cada oración por separado, no se genera ningún error en el INSERT ya que no se valida, pero el COMITÉ final se ejecuta donde encuentra una inconsistencia.
Para obtener información completa en inglés, le sugiero que consulte los enlaces:
Restricciones de SQL diferibles en profundidad
NO DEFERRABLE versus DEFERRABLE INICIALMENTE INMEDIATO
fuente