Usamos Oracle para tener nuestra información segura y accesible, pero si esta no es consistente pierde su valor. Por esta razón normalmente implementamos un mínimo de reglas que deben cumplir nuestros datos, esto mediante el uso de constraints. A saber son: not null, unique, primary key, foreign key y check.
Por defecto estas restricciones se validan en cuanto tratamos de modificar la información, lo cual en ocasiones nos puede limitar y obligarnos a tomar caminos alternativos, que pueden pasar incluso por deshabilitar los constraints de forma temporal. Esto introduce la posibilidad de generar información inconsistente, afortunadamente existe una forma de postergar estas validaciones, sin sacrificar la integridad de la información, y que aún sigue siendo poco conocida.
Para lograr nuestro objetivo de violar temporalmente las reglas impuestas por los constraints, debemos crearlos de una forma especial, conocida como deferrable constraints. Veamos la diferencia con el siguiente ejemplo, en el cual se crea un foreign key constraint con validación inmediata y luego con la modalidad postergada.
$ cat transaccion.sql
BEGIN
INSERT INTO emp (
empno, ename, job, mgr, hiredate, sal, comm, deptno )
VALUES (
8000, 'BROWN', 'MANAGER', NULL,
TO_DATE('15-JAN-1982', 'DD-MON-YYYY'), 3300, NULL, 50);
INSERT INTO dept ( deptno, dname, loc )
VALUES ( 50, 'MARKETING', 'MIAMI');
COMMIT;
END;
/
SCOTT@orcl> ALTER TABLE emp ADD CONSTRAINT emp_fk_dept
FOREIGN KEY(deptno) REFERENCES dept;
Table altered.
SCOTT@orcl> @transaccion
BEGIN
*
ERROR at line 1:
ORA-02291: integrity constraint (SCOTT.EMP_FK_DEPT) violated - parent key not found
ORA-06512: at line 2
Aún cuando el resultado final del bloque BEGIN..END es información consistente, la inmediatez de la validación impide ingresar una fila en la tabla EMP referenciando a un DEPTNO aún no existente en la tabla DEPT. Redefinamos entonces el constraint para que sea postergable.
SCOTT@orcl> ALTER TABLE emp ADD CONSTRAINT emp_fk_dept
FOREIGN KEY(deptno) REFERENCES dept
DEFERRABLE INITIALLY DEFERRED;
Table altered.
SCOTT@orcl> @transaccion
PL/SQL procedure successfully completed.
SCOTT@orcl> DELETE dept WHERE deptno = 50;
1 row deleted.
SCOTT@orcl> commit;
commit
*
ERROR at line 1:
ORA-02091: transaction rolled back
ORA-02292: integrity constraint (SCOTT.EMP_FK_DEPT) violated - child record found
SCOTT@orcl > ALTER TABLE emp ADD CONSTRAINT emp_fk_dept
FOREIGN KEY(deptno) REFERENCES dept
DEFERRABLE INITIALLY IMMEDIATE;
Como su nombre lo sugiere, IMMEDIATE conlleva a la validación del constraint en forma simultánea con la modificación de los datos, pero al ser el constraint DEFERRABLE, se permite postergar la validación para casos especiales, que se habilitan con una sintaxis adicional.
Veámoslo con el ejemplo.
SCOTT@orcl> @transaccion
BEGIN
*
ERROR at line 1:
ORA-02291: integrity constraint (SCOTT.EMP_FK_DEPT) violated - parent key not found
ORA-06512: at line 2
SCOTT@orcl > set constraint emp_fk_dept deferred;
Constraint set.
SCOTT@orcl> @transaccion
PL/SQL procedure successfully completed.
set constraint constraint_1, constraint_2, ..., constraint_n deferred;
set constraint all deferred;
Tenemos entonces en los deferrable constraints, una forma soportada de violar temporalmente las reglas que normalmente son validadas de inmediato, postergándolas hasta el momento en que concluimos la transacción (commit). Todo constraint puede ser postergado, pero en el caso particular del primary key y del unique, los índices que sirven para validar el cumplimiento de estos constraints son creados como índices no únicos, cuando normalmente son únicos. Esto implica que ocuparán algo más de espacio y su mantenimiento requerirá algo más de CPU, pero usualmente en tan pequeña magnitud que puede ser despreciado si lo comparamos con los eventuales beneficios.
Puedes leer más del tema en el manual Database Concepts y en el Note 73647.1 Deferred Constraints Example, también les recomiendo leer este Post por Richard Foote, en el que expone algunas razones por las que puede resultar inconveniente crear algunos tipos de constraints como postergables.