sql >> Base de Datos >  >> RDS >> Oracle

Actualizar Trigger PL/SQL Oracle

Pruebe con un activador compuesto:

CREATE OR REPLACE TRIGGER compound_trigger_name
FOR  INSERT OR UPDATE OF salary ON treballa
COMPOUND TRIGGER

  TYPE Departments_t   IS TABLE OF treballa.department%TYPE INDEX BY varchar2(100);
  Departments          Departments_t;

     BEFORE EACH ROW IS
     BEGIN
        -- collect updated or inserted departments 
        Departments( :new.department ) := :new.department;
     END BEFORE EACH ROW;

     AFTER STATEMENT IS
        sum_sal NUMBER;
     BEGIN
      -- for each updated department check the restriction
      FOR dept IN Departments.FIRST .. Departments.LAST
      LOOP
         SELECT sum(salary) INTO sum_sal FROM treballa WHERE department = dept;
         IF sum_sal > 1000 THEN
            raise_application_error(-20123, 'The total salary for department '||dept||' cannot exceed 1000');
         END IF;
      END LOOP;
     END AFTER STATEMENT;

END compound_trigger_name;
/

========EDIT - algunas preguntas y respuestas ===========

P:¿Por qué se produce un error de tabla mutante?
R:Esto se describe en la documentación:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#g1699708

P:¿Cómo evitar un error de tabla mutante?
R:La documentación recomienda el uso de un disparador compuesto, vea esto:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CHDFEBFJ

P:¿Qué es un disparador compuesto y cómo funciona?
R:Este es un tema muy amplio, consulte la documentación aquí:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHEFGFD

En resumen:este es un tipo especial de disparador que hace posible combinar cuatro tipos de disparadores separados:BEFORE statement , BEFORE-for each row , AFTER for each row y AFTER statament en una sola declaración. Facilita la implementación de algunos escenarios en los que es necesario pasar algunos datos de un disparador a otro. Estudie el enlace anterior para obtener más detalles.

P:Pero, ¿qué hace realmente "Departments( :new.department ) := :new.department; ?
R:Esta declaración almacena un número de departamento en una matriz asociativa.

Esta matriz se declara en una parte declarativa del disparador compuesto:

  TYPE Departments_t   IS TABLE OF treballa.department%TYPE INDEX BY varchar2(100);
  Departments          Departments_t;

La documentación relacionada con los disparadores compuestos dice que:http ://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHJBEFE

Lo anterior significa que Departments La variable se inicializa solo una vez al comienzo de todo el procesamiento, justo después de que se dispara el disparador. "Duración de la declaración de disparo" significa que esta variable se destruye después de que finaliza el disparador.

Esta declaración:Departments( :new.department ) := :new.department; almacena un número de departamento en la matriz asociativa. Está en BEFORE EACH ROW sección, luego se ejecuta para cada fila que se actualiza (o inserta) mediante la declaración de actualización/inserción.

:new y :old son pseudoregistros, puede encontrar más información aquí: http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS99955
En resumen::new.department recupera un nuevo valor de department columna- para una fila actualmente actualizada (valor actualizado - DESPUÉS de la actualización), mientras que :old.department da un valor antiguo de esta columna (ANTES de la actualización).

Esta colección se usa más adelante en AFTER STATEMENT , cuando los disparadores seleccionan todos los departamentos actualizados (en un FOR-LOOP), para cada departamento dispara SELECT SUM(salary) ... y luego verifica si esta suma es menor a 1000

Considere una actualización simple:UPDATE treballa SET salary = salary + 10 . Esta es una declaración de actualización única, pero cambia muchas filas a la vez. El orden de ejecución de nuestro disparador es el siguiente:

  1. Se activa la declaración de actualización:UPDATE treballa SET salary = salary + 10
  2. Se ejecuta la sección declarativa del disparador, es decir:Departments la variable se inicializa
  3. BEFORE EACH ROW La sección se ejecuta, por separado para cada fila actualizada, tantas veces como filas se actualicen. En este lugar recopilamos todos los departamentos de las filas modificadas.
  4. AFTER STATEMENT se ejecuta la sección. En este punto, la tabla ya está actualizada:todas las filas ya tienen salarios nuevos y actualizados. Recorremos los departamentos guardados en Departments y para cada uno verificamos si la suma de los salarios es menor o igual a 1000. Si esta suma es> 1000 para cualquiera de estos departamentos, se genera un error y se aborta y revierte toda la actualización. De lo contrario, el activador finaliza y la actualización finaliza (pero debe confirmar estos cambios de todos modos).

P:¿Qué es una matriz asociativa y por qué solo se usa este tipo de colección, en lugar de otras colecciones (varray o una tabla anidada)?
R:Las colecciones de PL/SQL son un tema enorme. Siga este enlace para conocerlos:http:// docs.oracle.com/cd/E11882_01/appdev.112/e25519/composites.htm#LNPLS005

En resumen:la matriz asociativa (o índice por tabla) es como un mapa en Java (hashmap, treemap, etc.):es un conjunto de pares clave-valor, y cada clave es único . Puede colocar la misma clave muchas veces en esta matriz (con diferentes valores), pero esta clave se almacenará solo una vez:es única.
La he usado para obtener un conjunto único de departamentos.
Considere nuevamente nuestro ejemplo de actualización:UPDATE treballa SET salary = salary + 10 - este comando toca cientos de filas que tienen el mismo departamento. No quiero una colección con el mismo departamento duplicado 100 veces, necesito un conjunto único de departamentos y quiero ejecutar nuestra consulta SELECT sum()... una sola vez para cada departamento, no 100 veces. Con la ayuda de la matriz sssociative, se hace automáticamente:obtengo un conjunto único de departamentos.