jueves, 13 de noviembre de 2008

Postergando la validación de los constraints.

Usamos Oracle para tener nuestra información segura y accesible, pero si ésta 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 lograrlo 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.

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
  2  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 table 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
  2  FOREIGN KEY(deptno) REFERENCES dept
  3  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
  2  FOREIGN KEY(deptno) REFERENCES dept
  3  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 concluímos 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á de 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 envíame un email y para que NO te pierdas ningún Post, suscríbete por email ahora mismo!

5 comentarios, agrega el tuyo!

Jorge dijo...

Hola (soy consultor SOA)
Me encanto esta explicación ya que no comprendia como se postergaba el constraint y el momento de su validación muy buen post.
saludos desde méxico

Enrique Orbegozo dijo...

Hola Surion,
Que bueno que este Post te haya sido útil, de qué parte de México eres? Hasta ahora solo conozco Cancún, bellísima ciudad con unas playas de ensueño y con gente muy amable que me hizo sentir como en casa.

Jorge dijo...

Hola de nuevo Enrique , si te comento que soy de el Estado de México muy cerca del DF , que bueno que te gustarón las playas de Cancún me han contado que son bellisimas espero ir pronto. Sin más me despido no sin antes felicitandote por tu blog ,de gran ayuda para los que estamos inmersos en el mundo Oracle.
saludos.

J dijo...
Este comentario ha sido eliminado por el autor.
J dijo...

excelente explicación