Obtener desde un disparador la referencia del objeto que se está insertando

Si disponemos de un disparador (trigger) de tipo before insert para poder acceder a las variables :new y :old, no disponemos de la posibilidad de acceder a la referencia del objeto en la base de datos ya que este no ha sido todavía insertado.

Para salvar esta posibilidad podemos dar un “rodeo” definiendo el disparador de tipo instead of. De esta forma el disparador sustituye a la orden que provoca su disparo, disponemos de las variables :new que nos proporcionan la información que se está insertando y  por tanto podemos reproducir la inserción añadiendo la cláusula RETURNING para que nos retorne la referencia del objeto que estamos insertando sin necesidad de consultar la tabla que aloja el objeto.

El problema surge debido a que tenemos la restricción de que sólo se pueden definir disparadores INSTEAD OF sobre vistas y por ello debemos crear una vista sobre la tabla

CREATE VIEW ALUMNOS_VIEW AS SELECT * FROM ALUMNOS_TAB

y sólo dar privilegios de acceso a la vista y no a la tabla base, por lo que las órdenes sólo se realizarán sobre la vista que hará saltar al disparado que realiza la inserción sobre la tabla base y el resto de acciones que sean necesarias.

CREATE TRIGGER INS
   INSTEAD OF INSERT ON ALUMNOS_VIEW
   FOR EACH ROW
DECLARE
   v_AlumnoRef REF Alumnos_objtab;
BEGIN
   ……………………………………..
   INSERT INTO alumnos_tab al VALUES (:new………)
   RETURNING REF(al) INTO v_AlumnoRef;
   …………………………………….
END;

A partir de ORACLE 11g no siempre es necesario evaluar con la orden SELECT sobre la tabla DUAL

Hasta ahora cada vez que necesitábamos obtener la información suministrada por las funciones estándar de SQL como SYSDATE necesitábamos utilizar la orden SELECT como “evaluador” como sigue:

DECLARE
    ahora DATE;
BEGIN
    SELECT SYSDATE INTO ahora
    FROM dual;
END;

Una situación análoga ocurre cuando necesitamos obtener el siguiente valor de una secuencia en PL/SQL:

DECLARE
    sig  tabla.claveprimaria%TYPE;
BEGIN
    SELECT mi_secuencia.NEXTVAL INTO sig
    FROM dual;
END;

A partir de la versión 11g se puede realizar la llamada directamente a NEXTVAL y CURRVAL en PL/SQL.

DECLARE
    sig  tabla.claveprimaria%TYPE;
BEGIN
    sig := mi_secuencia.NEXTVAL;
END;

Si utilizamos SELECT para evaluar una función de SQL, debemos plantearnos probar previamente si es posible utilizarla directamente en el código PL/SQL.

¿Se pueden referenciar objetos almacenados en una tabla anidada?

Consideremos el siguiente ejemplo basado en el diagrama UML mostrado a continuación:

Ej1_UML

En este ejemplo disponemos de tres tipos de objetos (clases), ESPACIOS que contiene una colección anidada de objetos de tipo ROSETA. A su vez el tipo DISPOSITIVOS referencia a la roseta en la que está conectado.

La implementación en SQL quedaría como sigue:

CREATE OR REPLACE TYPE Rosetas_objtyp AS OBJECT (
ID NUMBER(4),
ETIQUETA CHAR(4),
Estado VARCHAR2(8),
);

con el que creamos la clase Rosetas_objtyp. Para crear la colección de rostas y poder componerla en espacios creamos la colección Rosetas_ntabtyp

CREATE TYPE Rosetas_ntabtyp AS TABLE OF Rosetas_objtyp;

En la creación del tipo ESPACIOS incluimos el atributo del tipo colección creado anteriormente.

CREATE OR REPLACE TYPE Espacios_objtyp AS OBJECT (
ID NUMBER(4),
Ubicacion CHAR(4),
Tipo VARCHAR2(8),
Capacidad NUMBER(3),
Rosetas Rosetas_ntabtyp,
);

Ahora creamos el tipo DISPOSITIVO que pretende referenciar a objetos del tipo anidado Rosetas.

CREATE OR REPLACE TYPE Dispositivos_objtyp AS OBJECT (
Nombre VARCHAR2(10),
Descripcion VARCHAR2(30),
MAC VARCHAR2(17),
IP VARCHAR2(15),
Tipo CHAR(5)
Roseta REF Rosetas_objtyp
);

El siguiente paso es crear las tablas que permiten la persistencia de los objetos creados según los tipos definidos.

CREATE TABLE ESPACIOS_TAB OF ESPACIOS_OBJTYP
( ID PRIMARY KEY,  Capacicad CHECK (Capacidad > 0) )
  NESTED TABLE Rosetas STORE AS Rosetas_ntab ((
     PRIMARY KEY(ID) ));

CREATE TABLE DISPOSITIVOS_TAB OF DISPOSITIVOS_OBJTYP
( Nombre PRIMARY_KEY )

Una vez insertadas las tuplas en la tabla ESPACIOS_TAB, cuando procedemos a insertar un objeto de tipo dispositivo en la tabla de DISPOSITIVOS_TAB nos encontramos que no podemos obtener la referencia de la roseta en la que está conectado. Al realizar la orden

INSERT INTO DISPOSITIVOS_TAB
    VALUES ((‘LAN45′,’Reloj SS.GG.’,’0000.1232.1111′,’161.67.93.215′,’RLJ’,
                    (SELECT  REF(r)
                     FROM TABLE (SELECT RosetaS FROM ESPACIOS_TAB WHERE ID=1) r
                     WHERE Id = 1);

retorna error porque NO SE PUEDE OBTENER LA REFERENCIA de la roseta.

La razón es que roseta es una colección dentro del objeto Espacios_objtyp que es el que tiene definido el OID que permite referenciarlo. Las rosetas son un atributo colección dentro de espacio por lo que no puede ser referenciado.

La solución, en este caso, es realizar la referencia a la inversa, es decir, en lugar de que el dispositivo indique en que roseta está conectado, sea la roseta la que tenga la referencia al dispositivo.