All Things Oracle

Postergando la validación de los constraints

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.
Ahora sí se permite concluir la transacción, la validación se ha postergado al momento de hacer el commit, si en ese momento aún subsistiese alguna inconsistencia entonces recién se aborta la transacción.
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
Si bien permitir que la validación se postergue al momento de concluir la transacción resulta útil, por una cuestión de orden resulta más conveniente que ésta no sea la conducta por defecto, sino más bien la excepción, para ello debemos crear el constraint de otra forma.
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.
Con la inclusión de set constraint .. deferred, habilitamos temporalmente, y sólo para las transacciones al interior de la sesión vigente, la postergación del constraint “emp_fk_dept”. Podemos consignar toda una lista de constraints a postergar temporalmente o en su defecto indicar que lo sean todos los que puedan tratar de validarse en las futuras transacciones (siempre que hayan sido creadas como deferrable).
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.

¿Te pareció interesante este artículo?, ¿te quedaron algunas dudas?, ¿quieres sugerirme un tema a tratar?, pues déjame tus comentarios o ¡contáctame ahora mismo!

Agregue un comentario

Su dirección de correo no se hará público. Los campos requeridos están marcados *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Posts Relacionados

Primer post de una serie dedicada al arte de parchar Oracle. Empezamos con el parchado in-place, la forma más común y también la más peligrosa.
Aprenda a descargar los parches de Oracle, tanto manualmente como de forma automatizada, usando el utilitario getMOSPatch.
Link a articulo publicado en Toad World, sobre como aplicar un patch out-of-place a Grid Infrastructure, usando un Golden Image.

¿Necesitas Ayuda?

Completa estos datos y estaré en contacto a la brevedad.