Bloqueo de tablas hijo por causa de ejecutar sentencias PL/SQL sobre tablas padre
En versiones de la base de datos Oracle anteriores a la 9i, cuando la clave primaria de una tabla padre (parent table) no se encuentra indexada en la tabla hijo (child table), es muy probable que tengamosproblemas con los bloqueos de la tabla hijoque se producen, bien cuando se actualiza (con la sentencia PLSQL UPDATE) la clave primaria de la tabla padre (lo cual ocurre con relativa frecuencia ya que existen determinados trabajos que actualizan todas las columnas de una tabla incluso cuando el valor de la misma no ha cambiado), o bien cuando se realizaba el borrado (con la sentencia PL/SQL DELETE) de algn registro de la tabla padre.
El caso es que en las circunstancias anteriores y para evitar que se produzca un bloqueo completo de la tabla hijo (full table lock), lo ms recomendable es indexar tambin en la tabla hijo la clave primaria de la tabla padre. No obstante, esta norma de bloqueo cambi con la versin 9i de la base de datos Oracle.
La norma en Oracle 9i, aunque bsicamente el bloqueo completo no se puede evitar, lo que s que hace es disminuir sensiblemente el tiempo que dura dicho bloqueo. As, para versiones de la base de datos Oracle posteriores a la 9i, si se actualiza o se borra algn registro de una tabla padre cuya clave primaria no est indexada en la tabla hijo, dicha tabla hijo queda totalmente bloqueada slo mientras dura la ejecucin de las sentencias PLSQL UPDATE o DELETE. Es decir, el bloqueo se libera cuando las sentencias terminan y no es necesario esperar a que se ejecute la sentencia PL/SQL COMMIT. Por lo tanto, aunque la situacin es mejor que para versiones anteriores a Oracle 9i, el bloqueo completo de la tabla hijo todava ocurre.
Tambin conviene sealar que este bloqueo completo de la tabla hijo tambin ocurre cuando se ejecuta la sentencia PLSQL MERGE (sentencia que apareci precisamente con la versin de la base de datos Oracle 9i). No obstante, despus de la versin Oracle 11g Release 1, la ejecucin de una sentencia PL/SQL MERGE no siempre produce el bloqueo de la tabla hijo, esto ocurre cuando el MERGE es simplemente un INSERT, o cuando el MERGE funciona tambin como un UPDATE pero dicho UPDATE no cambia la clave primaria de la tabla padre.
A continuacin os dejo un script SQL con el que es posible detectar las claves primarias que no estn indexadas en las tablas hijo correspondientes (por cierto, en ingls las claves primarias de una tabla padre se conocen, desde el punto de vista de las tablas hijos, como foreign keys o claves extranjeras). El script slo funciona para claves primarias que incluyen un mximo de ocho columnas. SELECT nombre_tabla, nombre_constraint, cnom1 || NVL2(cnom2,','||cnom2,NULL) || NVL2(cnom3,','||cnom3,NULL) || NVL2(cnom4,','||cnom4,NULL) || NVL2(cnom5,','||cnom5,NULL) || NVL2(cnom6,','||cnom6,NULL) || NVL2(cnom7,','||cnom7,NULL) || NVL2(cnom8,','||cnom8,NULL) columnas FROM ( SELECT uc.table_name nombre_tabla, uc.constraint_name nombre_constraint, MAX(DECODE(pos,1,coln,NULL)) cnom1, MAX(DECODE(pos,2,coln,NULL)) cnom2, MAX(DECODE(pos,3,coln,NULL)) cnom3, MAX(DECODE(pos,4,coln,NULL)) cnom4, MAX(DECODE(pos,5,coln,NULL)) cnom5, MAX(DECODE(pos,6,coln,NULL)) cnom6, MAX(DECODE(pos,7,coln,NULL)) cnom7, MAX(DECODE(pos,8,coln,NULL)) cnom8, count(*) ncol FROM ( SELECT SUBSTR(table_name,1,30) tn, SUBSTR(constraint_name,1,30) cn, SUBSTR(column_name,1,30) coln, position pos FROM user_cons_columns ) ucc, user_constraints uc WHERE ucc.cn = uc.constraint_name AND uc.constraint_type = 'R' GROUP BY uc.table_name, uc.constraint_name ) user_cons WHERE ncol > ALL ( SELECT count(*) FROM user_ind_columns uic WHERE uic.table_name = user_cons.nombre_tabla AND uic.column_name in (cnom1, cnom2, cnom3, cnom4, cnom5, cnom6, cnom7, cnom8) AND uic.column_position <= user_cons.ncol GROUP BY uic.index_name )
En conclusin, la clara recomendacin que personalmente doy es que, si sobre una tabla padre vamos a realizar operaciones de actualizacin de la clave primaria, borrado de registros o merge (insercin + actualizacin + borrado), lo mejor es proceder a indexar dicha clave primaria en la tabla hijo. Adems, otra circunstancia que hace recomendable la indexacin de la clave primaria de la tabla padre en la tabla hijo, es que no hacerlo puede derivar en graves problemas de rendimiento.
Las claves forneas producen bloqueos en Forms? La respuesta es que si no hacemos bien las cosas es SI. Vamos a verlo mejor con un ejemplo con las tpicas tablas de formacin que utiliza Oracle, DEPT y EMP:
CREATE TABLE dept ( deptno NUMBER(2) NOT NULL CONSTRAINT dept_pk PRIMARY KEY, dname VARCHAR2(14), loc VARCHAR2(13));
CREATE TABLE emp ( empno NUMBER(4) NOT NULL CONSTRAINT emp_pk PRIMARY KEY, ename VARCHAR2(10), job VARCHAR2(9), mgr NUMBER(4), hiredate DATE, sal NUMBER(7,2), comm NUMBER(7,2), deptno NUMBER(2) CONSTRAINT emp_ref_dept_fk REFERENCES dept(deptno));
Insertamos un departamento:
INSERT INTO dept (deptno, dname, loc) VALUES (23, 'CONTABILIDAD', 'VIGO');
Antes de hacer el commit, al ver los bloqueos veremos que estn la tabla EMP y la DEPT bloqueadas.
SELECT lk.sid, lk.TYPE lock_type, lk.lmode, (SELECT o.object_name FROM dba_objects o WHERE o.object_id = lk.id1) object_name FROM gv$lock lk WHERE lk.TYPE IN ('TM', 'UL');
El bloqueo de la tabla EMP es por el registro que acabamos de insertar pero que todava no est commitado, pero la tabla DEPT se bloquea por la clave fornea, ya que en el momento de insertar el registro en EMP exista el departamento 23 en DEPT, pero como no hemos comitado el insert en EMP tiene que bloquear el registro del departamento 23 para evitar que otra sesin borre ese registro o modifique el cdigo del departamento.
Hasta aqu todo normal, el problema viene cuando otra sesin quiere modificar el nombre del departamento:
UPDATE dept SET dname = 'FINANCIERO' WHERE deptno = 23;
Con esta update nos dejar modificarlo sin problema, pero si en vez de hacer un update que nicamente modifica el nombre del departamento hacemos este otro (el resultado final de los datos en la tabla sern los mismos):
UPDATE dept SET deptno = 23, dname = 'FINANCIERO', loc = 'VIGO' WHERE deptno = 23;
El registro quedara con los mismos datos, pero ya tenemos el bloqueo montado ya que le estamos diciendo que actualice el campo DEPTNO, pero la modificacin de ese campo afectara a registros introducidos por otra sesin y que todava no estn comitados, por lo que se quedar bloqueado.
Esta es la explicacin del motivo, pero porqu ocurre en Forms?. La respuesta la encontramos en la propiedad "Actualizar Slo Columnas Cambiadas" a nivel de bloque, por defecto esa propiedad est a "No" con lo que para hacer un update actualizar todos los campos, incluso los que no ha modificado el usuario.
Lo recomendable para evitar bloqueos es poner esa propiedad al valor "Si".