Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Atualizar gatilho PL/SQL Oracle


Tente um gatilho composto:
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 - algumas perguntas e respostas ===========

P:Por que ocorre um erro de tabela mutante?
R:Isso está descrito na documentação:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#g1699708

P:como evitar um erro de tabela mutante?
R:A documentação recomenda o uso de um gatilho composto, veja isto:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CHDFEBFJ


P:O que é um acionador composto e como ele funciona?
R:Este é um tópico enorme, consulte a documentação aqui:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHEFGFD

Resumindo:este é um tipo especial de gatilho que torna o psiable combinar quatro tipos de gatilhos separados:BEFORE statement , BEFORE-for each row , AFTER for each row e AFTER statament em uma declaração. Facilita a implementação de alguns cenários em que há a necessidade de passar alguns dados de uma trigger para outra. Por favor, estude o link acima para mais detalhes.

P:Mas o que realmente faz "Departments( :new.department ) := :new.department; ?
R:Esta declaração armazena um número de departamento em um array associativo.

Este array é declarado em uma parte declarativa do gatilho composto:
  TYPE Departments_t   IS TABLE OF treballa.department%TYPE INDEX BY varchar2(100);
  Departments          Departments_t;

A documentação relacionada aos gatilhos compostos diz que:http ://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHJBEFE

O acima significa que Departments A variável é inicializada apenas uma vez no início de todo o processamento, logo após o disparo do gatilho. "Duração da instrução de disparo" significa que esta variável é destruída após o término do gatilho.

Esta instrução:Departments( :new.department ) := :new.department; armazena um número de departamento na matriz associativa. Está em BEFORE EACH ROW seção, então ele é executado para cada linha que é atualizada (ou inserida) pela instrução update/insert.

:new e :old são pseudoregistros, mais sobre eles você pode encontrar aqui: http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS99955
Resumindo::new.department recupera um novo valor de department column- para uma linha atualmente atualizada (valor atualizado - APÓS a atualização), enquanto :old.department fornece um valor antigo desta coluna (ANTES da atualização).

Esta coleção é usada posteriormente na AFTER STATEMENT , quando os gatilhos selecionam todos os departamentos atualizados (em um FOR-LOOP), para cada departamento dispara SELECT SUM(salary) ... e então verifica se esta soma é menor que 1000

Considere uma simples atualização:UPDATE treballa SET salary = salary + 10 . Esta é uma única instrução de atualização, mas altera muitas linhas de uma vez. A ordem de execução do nosso gatilho é a seguinte:
  1. A atualização de status é acionada:UPDATE treballa SET salary = salary + 10
  2. A seção declarativa do gatilho é executada, ou seja:Departments variável é inicializada
  3. BEFORE EACH ROW é executada separadamente para cada linha atualizada - quantas vezes houver linhas a serem atualizadas. Neste local, coletamos todos os departamentos das linhas alteradas.
  4. AFTER STATEMENT seção é executada. Neste ponto, a tabela já está atualizada - todas as linhas já possuem salários novos e atualizados. Percorremos os departamentos salvos em Departments e para cada um verificamos se a soma dos salários é menor ou igual a 1.000. Se essa soma for> 1.000 para qualquer um desses departamentos, um erro será lançado e toda a atualização será abortada e revertida. Caso contrário, o gatilho termina e a atualização é feita (mas você precisa confirmar essas alterações de qualquer maneira).

P:O que é um array associativo e por que apenas esse tipo de coleção é usado, em vez de outras coleções (um varray ou uma tabela aninhada)?
R:As coleções PL/SQL são um grande tópico. Siga este link para aprender:http:// docs.oracle.com/cd/E11882_01/appdev.112/e25519/composites.htm#LNPLS005

Resumindo - array associativo (ou index-by table) é como um mapa em java (hashmap, treemap etc) - é um conjunto de pares chave-valor, e cada chave é único . Você pode colocar a mesma chave várias vezes nesse array (com valores diferentes), mas essa chave será armazenada apenas uma vez - ela é única.
Eu a usei para obter um conjunto exclusivo de departamentos.
Considere nosso exemplo de atualização novamente:UPDATE treballa SET salary = salary + 10 - este comando toca centenas de linhas que possuem o mesmo departamento. Não quero uma coleção com o mesmo departamento duplicado 100 vezes, preciso de um conjunto único de departamentos e quero executar nossa consulta SELECT sum()... apenas uma vez para cada departamento, não 100 vezes. Com a ajuda do array associativo, isso é feito automaticamente - recebo um conjunto exclusivo de departamentos.