Oracle PL/SQL Estructura - UDCOracle PL/SQL Miguel Rodríguez Penabad Laboratorio de Bases de Datos...

41

Transcript of Oracle PL/SQL Estructura - UDCOracle PL/SQL Miguel Rodríguez Penabad Laboratorio de Bases de Datos...

  • Oracle PL/SQL

    Miguel Rodríguez Penabad

    Laboratorio de Bases de Datos

    Universidade da Coruña

    Curso 2011-2012

    Estructura

    Bloques anónimos

    DECLARE/* Sección declarativa - variables PL/SQL, tipos,

    cursores, subprogramas locais */BEGIN

    /* Sección executable - ordes PL/SQL */EXCEPTION

    /* Sección de manexo de excepcións */END;

    Exemplo mínimo

    BEGINNULL; -- Necesitamos polo menos unha sentencia.

    -- NULL é unha sentencia que non fai nadaEND;/

    Oracle PL/SQL [Curso 2011-2012] 2 / 50

  • Exemplo 1: Ver a data actual

    I SET SERVEROUTPUT ONI DBMS_OUTPUT.PUT_LINEI Acabamos o bloque con . e executamos (R ou RUN)I Se acabamos con /, executa directamente

    SQL> BEGIN2 DBMS_OUTPUT.PUT_LINE(SYSDATE);3 END;4 .

    SQL> R02/04/06Procedimiento PL/SQL terminado correctamente.

    Oracle PL/SQL [Curso 2011-2012] 3 / 50

    Variables e tipos

    Tipos prede�nidos

    Tipos de SQL máis BOOLEAN, BINARY_INTEGER

    DECLARENome VARCHAR2 (20);" Unha variable cun nome rarísimo" NUMBER(1);Contador BINARY_INTEGER;ProcesoFinalizado BOOLEAN;

    Tipos de�nidos polo usuario (rexistros)

    DECLARETYPE t_RexistroEstudiante IS RECORD (

    Nome VARCHAR2(20),Apelido1 VARCHAR2(20),Apelido2 VARCHAR2(20));

    variable_Estudiante t_RexistroEstudiante;

    Oracle PL/SQL [Curso 2011-2012] 4 / 50

  • Variables e tipos (ii)

    Sintaxe completa

    [CONSTANT] [NOT NULL] [{:=|DEFAULT} ];

    DECLAREnum1 NUMBER;fecha1 DATE :=SYSDATE;num3 NUMBER DEFAULT 3;num4 CONSTANT NUMBER:=4;num5 NUMBER NOT NULL DEFAULT 5;num6 CONSTANT NUMBER NOT NULL := 6;

    Usar o tipo doutra variable, dun atributo ou dunha táboa

    DECLAREsalario NUMBER(7,2); -- Problema se cambia a definición na táboa

    Mellor:

    DECLAREsalario EMP.SAL%TYPE;soldo salario%TYPE;rEmp EMP%ROWTYPE;

    Oracle PL/SQL [Curso 2011-2012] 5 / 50

    Sección executable

    Manipular variables

    I Asignación: :=I Dependendo do tipo:

    I Operacións matemáticas (+,-,*,/,...), concatenación (||)I Funcións (power, upper, length, ...)I etc.

    DECLAREn BINARY_INTEGER;string VARCHAR(20);

    BEGINn := 3;DBMS_OUTPUT.PUT_LINE('n vale '||n);n := power(2,3);DBMS_OUTPUT.PUT_LINE('n vale 2 elvado a 3: '||n);string := 'Un tExTo';DBMS_OUTPUT.PUT_LINE(string||' en maiúsculas é '|| upper(string));

    END;/n vale 3n vale 2 elvado a 3: 8Un tExTo en maiúsculas é UN TEXTO

    Oracle PL/SQL [Curso 2011-2012] 6 / 50

  • SQL en bloques PL/SQL

    I Podemos incluir variables na sentencia SQLI Podemos usar DML

    I Insert, delete, updateI Para o SELECT teremos varios casos

    I O uso de DDL será de forma distinta

    I A execución dun bloque PL/SQL é �atómica�I Igual que a execución dunha sentencia SQL.I Ex: se nun bloque hai 2 inserts e o segundo falla, o primeiro

    non se conserva.

    Oracle PL/SQL [Curso 2011-2012] 7 / 50

    SQL en bloques PL/SQL (ii)

    Exemplo

    I Crea unha táboa PERSOA(IDPERSOA,NOME,IDADE)I Crea un bloque de código para insertar datos

    I DirectamenteI Lendo os datos e almacenándoos nun rexistro

    create table persoa(idpersoa char(12) not null primary key,nome char(20), idade number(3));

    DECLARErPersoa persoa%ROWTYPE;

    BEGININSERT INTO PERSOA(IDPERSOA,NOME,IDADE)

    VALUES ('01234567T', 'Carpanta', 50);INSERT INTO PERSOA(IDPERSOA,NOME,IDADE)

    VALUES ('3456789B', 'Sacarino', 17);

    rPersoa.idpersoa := '12345678A';rPersoa.nome := 'Matusalén';rPersoa.idade := 850;INSERT INTO PERSOA

    VALUES rPersoa;END;

    Oracle PL/SQL [Curso 2011-2012] 8 / 50

  • SQL en bloques PL/SQL (iii)

    Exemplo

    I Actualiza a idade de todas as persoas, engadindo un anodeclare

    rPersoa persoa%ROWTYPE;begin

    -- Campos individuaisUPDATE PERSOA SET IDADE = IDADE + 1;

    -- Toda a filarPersoa.idpersoa := '12345678A';rPersoa.nome := 'Matusalén';rPersoa.idade := 850;

    UPDATE PERSOA SET ROW = rPersoaWHERE idpersoa = '12345678A';

    end;

    I Borra as persoas cun identi�cador que acabe en Tbegin

    DELETE FROM PERSOA WHERE IDPERSOA LIKE '%T';end;

    Oracle PL/SQL [Curso 2011-2012] 9 / 50

    SQL en bloques PL/SQL (iv)

    Selección de datos

    I Uso da cláusula SELECT ... INTOI Con variables ou rexistros PL/SQLI Obtén e imprime información da persoa con IDPERSOA

    '12345678A'

    DECLARErPersoa PERSOA%ROWTYPE;

    BEGINSELECT *

    INTO rPersoaFROM PERSOAWHERE IDPERSOA='12345678A';

    DBMS_OUTPUT.PUT_LINE(rPersoa.IDPERSOA ||' '||rPersoa.NOME ||' '||rPersoa.IDADE);

    END;/

    12345678A Matusalén 851

    Oracle PL/SQL [Curso 2011-2012] 10 / 50

  • SQL en bloques PL/SQL (vi)

    Selección de datos (cont.)

    I Obtén e imprime información da(s) persoa(s) de 101 anos

    DECLARErPersoa PERSOA%ROWTYPE;

    BEGINSELECT * INTO rPersoa

    FROM PERSOAWHERE IDADE = 101;

    DBMS_OUTPUT.PUT_LINE(rPersoa.IDPERSOA ||' '||rPersoa.NOME ||' '||rPersoa.IDADE);

    END;

    I Excepción NO_DATA_FOUNDERROR en línea 1:ORA-01403: no se han encontrado datosORA-06512: en línea 4

    Oracle PL/SQL [Curso 2011-2012] 11 / 50

    SQL en bloques PL/SQL (v)

    Selección de datos (cont.)

    I Obtén e imprime información da(s) persoa(s) de máis de 15anos

    DECLARErPersoa PERSOA%ROWTYPE;

    BEGINSELECT * INTO rPersoa

    FROM PERSOAWHERE IDADE > 15;

    DBMS_OUTPUT.PUT_LINE(rPersoa.IDPERSOA ||' '||rPersoa.NOME ||' '||rPersoa.IDADE);

    END;

    I Excepción TOO_MANY_ROWSERROR en línea 1:ORA-01422: la recuperación exacta devuelve un número

    mayor de filas que el solicitadoORA-06512: en línea 4

    Oracle PL/SQL [Curso 2011-2012] 12 / 50

  • SQL en bloques PL/SQL (vii)

    Selección de datos (cont.)

    I O SELECT ... INTO só pode usarse se sabemos que imosrecuperar unha �la

    I Exemplos: funcións de agregación (SUM, AVG, COUNT, ...)

    DECLAREnumero NUMBER;media NUMBER;

    BEGINSELECT COUNT(*), AVG(IDADE)

    INTO numero, mediaFROM PERSOA;

    DBMS_OUTPUT.PUT_LINE('Hai '|| numero || ' persoas');DBMS_OUTPUT.PUT_LINE('A idade media é de '|| numero || ' anos');

    END;/

    Hai 3 persoasA idade media é de 3 anos

    Oracle PL/SQL [Curso 2011-2012] 13 / 50

    Estructuras de Control

    Condicional

    I SimpleIF THEN

    END IF;

    I Sintaxe completaIF THEN

    /* Accións */[ELSIF THEN

    /* Accións */... ]

    [ELSE/* Accións se todas as condicións

    anteriores non son certas */ ]END IF;

    Oracle PL/SQL [Curso 2011-2012] 14 / 50

  • Estructuras de Control (ii)

    Condicionais. Exercicio

    I Escribe un bloque de código que indique se a media de idade ésuperior ou non a 25 anos.

    I Funciona IF (SELECT AVG(IDADE) ....)?I NonI Hai que usar SELECT ... INTO

    DECLAREmedia NUMBER;

    BEGINSELECT AVG(IDADE) INTO media

    FROM PERSOA;IF media > 25 THEN

    DBMS_OUTPUT.PUT_LINE('Idade media superior a 25');ELSE

    DBMS_OUTPUT.PUT_LINE('Non é superior a 25');END IF;

    END;/Idade media superior a 25

    Oracle PL/SQL [Curso 2011-2012] 15 / 50

    Estructuras de Control (iii)

    Bucles

    I Simple: LOOP ... END LOOPDECLARE

    contador BINARY_INTEGER := 1;BEGIN

    LOOP -- Bucle infinito!DBMS_OUTPUT.PUT_LINE(contador);contador := contador + 1;

    END LOOP;END;

    Oracle PL/SQL [Curso 2011-2012] 16 / 50

  • Estructuras de Control(iv)

    Bucles

    I Introducir unha condición e usar EXITDECLARE

    contador BINARY_INTEGER := 1;BEGIN

    LOOPIF contador > 10 THEN

    EXIT;END IF;DBMS_OUTPUT.PUT_LINE(contador);contador := contador + 1;

    END LOOP;END;

    I Mellor: Usar EXIT WHEN LOOPEXIT WHEN contador > 10;...

    END LOOP;

    Oracle PL/SQL [Curso 2011-2012] 17 / 50

    Estructuras de Control(iv)

    Bucles anidados. Etiquetas

    Etiqueta: Localiza a seguinte liña de código.

    DECLAREcont1 BINARY_INTEGER := 1;cont2 BINARY_INTEGER := 1;

    BEGIN

    LOOPDBMS_OUTPUT.PUT_LINE('Bucle externo: '||cont1);cont2:=1;

    LOOPDBMS_OUTPUT.PUT(' Int:'||cont2); cont2:=cont2+1;EXIT interno WHEN cont2>cont1;

    END LOOP;DBMS_OUTPUT.NEW_LINE; cont1 := cont1 + 1;EXIT externo WHEN cont1 > 10;

    END LOOP;END;

    Oracle PL/SQL [Curso 2011-2012] 18 / 50

  • Estructuras de Control(v)

    Bucles WHILE

    I Sintaxe: WHILE LOOP ... END LOOP:I Comproba a condición antes de entrar no bucleI Exemplo:

    DECLAREcontador BINARY_INTEGER := 1;

    BEGINWHILE contador

  • Consultas e cursores (i)

    Unha �la

    I Se sabemos a priori que a consulta devolve exactamente unha�la:SELECT INTO ;

    SELECT INTO ;

    Número indeterminado de �las

    I Uso de CURSORes1. Declarar cursor2. Abrir cursor3. (Bucle) Procesar �las4. Cerrar cursor

    Oracle PL/SQL [Curso 2011-2012] 21 / 50

    Consultas e cursores (ii)

    Declaración

    I Na sección de declaración de variablesI Sintaxe: CURSOR IS ;

    I Aparece o tipo %ROWTYPE.

    I A consulta pode incluir variables

    Exemplos

    DECLARECURSOR c_xente IS

    SELECT * FROM PERSOA;

    CURSOR c_xubilados ISSELECT * FROM PERSOA

    WHERE IDADE >= 65;

    limiteIdade PERSOA.IDADE%TYPE;CURSOR c_xente_maior_de IS

    SELECT * FROM PERSOAWHERE IDADE >= limiteIdade;

    rPersoa c_xente_maior_de%ROWTYPE; -- Sería igual que PERSOA%ROWTYPE

    Oracle PL/SQL [Curso 2011-2012] 22 / 50

  • Consultas e cursores (iii)

    Apertura do cursor

    I Abrir o cursor: OPEN ;I Se a consulta incluía variables debemos darlles o valor antes de

    abrir o cursor.I Oracle executa a consulta e usa a área e contexto

    I Información sobre a consultaI Os datos obtidos da consulta (�conxunto activo�)

    Exemplo

    DECLARElimiteIdade PERSOA.IDADE%TYPE;CURSOR c_xente_maior_de IS

    SELECT * FROM PERSOA WHERE IDADE >= limiteIdade;BEGIN

    limiteIdade := 65;OPEN c_xente_maior_de;...

    END;

    Oracle PL/SQL [Curso 2011-2012] 23 / 50

    Consultas e cursores (iv)

    Cerre do cursor

    I Cerrar o cursor: CLOSE ;I Oracle libera a área de contexto asociada

    Exemplo

    DECLARElimiteIdade PERSOA.IDADE%TYPE;CURSOR c_xente_maior_de IS

    SELECT * FROM PERSOA WHERE IDADE >= limiteIdade;rPersoa c_xente_maior_de%ROWTYPE;

    BEGINlimiteIdade := 35;OPEN c_xente_maior_de;...CLOSE c_xente_maior_de;

    END;

    Oracle PL/SQL [Curso 2011-2012] 24 / 50

  • Consultas e cursores (v)

    Procesar �las

    I Sentencia para obter unha �la:FETCH INTO {|};

    I Normalmente dentro dun bucle

    Propiedades dos cursores

    %ISOPEN:I Indica se o cursor está aberto.I Se %ISOPEN é certo, podemos consultar as

    seguintes propiedades.

    %FOUNDI O último FETCH obtivo unha �la.

    %NOTFOUNDI O último FETCH non obtivo ningunha �la.

    %ROWCOUNTI Número de �las procesadas actualmente.

    I As tres últimas propiedades tenen un signi�cado especial parao cursor SQL

    Oracle PL/SQL [Curso 2011-2012] 25 / 50

    Consultas e cursores (vi)

    Procesar �las (bucle estándar)

    DECLARElimiteIdade PERSOA.IDADE%TYPE;CURSOR c_xente_maior_de IS

    SELECT * FROM PERSOA WHERE IDADE >= limiteIdade;rPersoa c_xente_maior_de%ROWTYPE;

    BEGINlimiteIdade := 35;OPEN c_xente_maior_de;LOOP

    FETCH c_xente_maior_deINTO rPersoa;

    EXIT WHEN c_xente_maior_de%NOTFOUND;DBMS_OUTPUT.PUT_LINE(rPersoa.idpersoa||', '||rPersoa.nome||', '||rPersoa.idade);

    END LOOP;CLOSE c_xente_maior_de;

    END;/

    01234567T , Carpanta , 5112345678A , Matusalén , 851

    Oracle PL/SQL [Curso 2011-2012] 26 / 50

  • Consultas e cursores (vii)

    Procesar �las (bucle while)

    Lectura adiantada:

    DECLARElimiteIdade PERSOA.IDADE%TYPE;CURSOR c_xente_maior_de IS

    SELECT * FROM PERSOAWHERE IDADE >= limiteIdade;

    rPersoa c_xente_maior_de%ROWTYPE;BEGIN

    limiteIdade := 35;OPEN c_xente_maior_de;

    FETCH c_xente_maior_de INTO rPersoa;WHILE c_xente_maior_de%FOUND LOOP

    DBMS_OUTPUT.PUT_LINE(rPersoa.idpersoa||', '||rPersoa.nome||', '||rPersoa.idade);FETCH c_xente_maior_de INTO rPersoa;

    END LOOP;

    CLOSE c_xente_maior_de;END;

    Oracle PL/SQL [Curso 2011-2012] 27 / 50

    Consultas e cursores (viii)

    Procesar �las (bucle for con cursor explícito)

    I Sintaxe:FOR IN LOOP

    /* Procesar fila actual */END LOOP;

    I Consideracións importantesI O non se declara, é interno ó FORI Non abrimos (OPEN) o cursor antes do bucleI Non cerramos (CLOSE) o cursor despois do bucleI Non facemos FETCH dentro do bucleI Non facemos comprobación (%NOTFOUND) de �nalización

    DECLARElimiteIdade PERSOA.IDADE%TYPE;CURSOR c_xente_maior_de IS

    SELECT * FROM PERSOA WHERE IDADE >= limiteIdade;BEGIN

    limiteIdade := 35;FOR rPersoa IN c_xente_maior_de LOOP

    DBMS_OUTPUT.PUT_LINE(rPersoa.idpersoa||', '||rPersoa.nome||', '||rPersoa.idade);END LOOP;

    END;

    Oracle PL/SQL [Curso 2011-2012] 28 / 50

  • Consultas e cursores (ix)

    Procesar �las (bucle for con cursor implícito)

    I Sintaxe:FOR IN () LOOP

    /* Procesar fila actual */END LOOP;

    I ConsideraciónsI Comportamento similar ó bucle FOR con cursor explícito.I Non declaramos o cursor, usamos directamente a consulta.I A consulta debe ir entre parénteses.

    DECLARElimiteIdade PERSOA.IDADE%TYPE;

    BEGINlimiteIdade := 35;FOR rPersoa IN (SELECT * FROM PERSOA WHERE IDADE >= limiteIdade)LOOP

    DBMS_OUTPUT.PUT_LINE(rPersoa.idpersoa||', '||rPersoa.nome||', '||rPersoa.idade);END LOOP;

    END;

    Oracle PL/SQL [Curso 2011-2012] 29 / 50

    Consultas e cursores (x)

    Cursor implícito �SQL�

    I Usado para INSERT, UPDATE, DELETE e SELECT INTOI Non abrimos, cerramos, nin facemos FETCHI Podemos consultar as propiedades

    SQL%FOUND: hai �las afectadasSQL%NOTFOUND: non hai �las afectadasSQL%ROWCOUNT: no de �las afectadas(SQL%ISOPEN sempre é falso)

    Exemplo

    Borra todas as persoas que teñan menos de 10 anos. Se non hainingunha, amosa unha mensaxe indicándoo.

    BEGINDELETE FROM PERSOA WHERE IDADE

  • Consultas e cursores (xi)

    Cursores actualizables

    I Declaramos o cursor FOR UPDATE [NOWAIT]I Bloquéanse todas as �las recuperadas (ata o commit).I Especi�cando NOWAIT obtemos excepción se non pode

    bloquear as �las.I Usamos WHERE CURRENT OF como condición

    para actualizar a �la actual

    Exemplo

    Restar 1 á Idade das persoas con 77 anos.

    DECLARECURSOR c_persoas IS

    SELECT * FROM PERSOAFOR UPDATE NOWAIT;

    BEGINFOR rPersoa in c_persoas LOOP

    IF rPersoa.idade = 77 THENUPDATE PERSOA SET IDADE = IDADE - 1

    WHERE CURRENT OF c_persoas;END IF;

    END LOOP;END;

    Oracle PL/SQL [Curso 2011-2012] 31 / 50

    SQL dinámico (i)

    Sentencias DDL

    I Sintaxe:BEGIN

    EXECUTE IMMEDIATE ;END;

    Exemplo

    DECLAREcomando char(150);

    BEGINcomando := 'create table tabla (campo char)';EXECUTE IMMEDIATE comando;

    EXECUTE IMMEDIATE 'create index idxcampo on tabla(campo)';END;

    Oracle PL/SQL [Curso 2011-2012] 32 / 50

  • SQL dinámico (ii)

    Exemplo

    Problemas mezclando SQL estático e dinámico:O bloque PL/SQL non compila se tratamos de usar a táboa que sevai crear con SQL dinámico.

    DECLAREcomando char(150);

    BEGINcomando := 'create table tabla (campo char)';EXECUTE IMMEDIATE comando;EXECUTE IMMEDIATE 'create index idxcampo on tabla(campo)';

    -- FallaráINSERT INTO TABLA VALUES('A');

    END;/

    INSERT INTO TABLA VALUES('A');*

    ERROR en línea 8:ORA-06550: línea 8, columna 16:PL/SQL: ORA-00942: la tabla o vista no existeORA-06550: línea 8, columna 4:PL/SQL: SQL Statement ignored

    Oracle PL/SQL [Curso 2011-2012] 33 / 50

    SQL dinámico (iii)

    ExercicioModi�ca o código anterior de forma que funcione correctamente.

    DECLAREcomando char(150);

    BEGINcomando := 'create table tabla (campo char)';EXECUTE IMMEDIATE comando;EXECUTE IMMEDIATE

    'create index idxcampo on tabla(campo)';

    EXECUTE IMMEDIATE'INSERT INTO TABLA VALUES(''A'')';

    END;

    Oracle PL/SQL [Curso 2011-2012] 34 / 50

  • SQL dinámico (iv)

    Variables tipo cursor

    I Tipo xenérico: SYS_REFCURSOR ou REF CURSORI Poden usarse con SQL dinámico.

    I Tipo especí�co: REF CURSOR RETURN I Uso: paso de coleccións de datos como parámetros en

    subprogramas.(Non sirven para SQL dinámico) (Non os estudiaremos)

    I Declaramos cursor dese tipo especí�coI Construimos a cadea de caracteres da sentenciaI Abrimos o cursor para a sentencia

    DECLARE-- Definición alternativa:-- refc ref cursor; c_xente refc;c_xente SYS_REFCURSOR;rex PERSOA%ROWTYPE;

    BEGINOPEN c_xente FOR 'SELECT * FROM PERSOA';LOOP

    FETCH c_xente INTO rex;EXIT WHEN c_xente%NOTFOUND;DBMS_OUTPUT.PUT_LINE(rex.idpersoa||', '||rex.nome||', '||rex.idade);

    END LOOP;CLOSE c_xente;

    END;

    Oracle PL/SQL [Curso 2011-2012] 35 / 50

    Excepcións (i)

    SQLCODEInforma sobre a última operación SQL do bloque de códigoPL/SQL

    I SQLCODE = 0 : última operación con éxitoI SQLCODE < 0 : Erro na última operación

    SQLERRMMensaxe de erro correspondente ó SQLCODE

    BEGINDBMS_OUTPUT.PUT_LINE('Código: '||SQLCODE);DBMS_OUTPUT.PUT_LINE('Mensaxe: '||SQLERRM);

    END;/Código: 0Mensaxe: ORA-0000: normal, successful completion

    Oracle PL/SQL [Curso 2011-2012] 36 / 50

  • Excepcións (ii)

    Estrutura dun bloque PL/SQL

    BEGIN/* Código problemático */

    EXCEPTION[ WHEN THEN

    -- Accións se ocurriu excepcion1 ][ WHEN THEN

    -- Accións se ocurriu excepcion2 ][ WHEN OTHERS THEN

    -- Accións se ocurriu calquera excepcion ]END;

    I Necesitamos polo menos un WHEN ...I Usamos o nome da excepción

    (NO_DATA_FOUND, TOO_MANY_ROWS, ZERO_DIVIDE,DUP_VAL_ON_INDEX, ...)

    I Usamos OTHERS para o caso xenérico

    I Compróbanse por ordeI Dentro de cada excepción, escribimos código PL/SQL normal

    I RAISE relanza a excepción.

    Oracle PL/SQL [Curso 2011-2012] 37 / 50

    Excepcións (iii)

    Exemplo

    DECLARENOME PERSOA.NOME%TYPE;

    BEGINSELECT NOME

    INTO NOMEFROM PERSOAWHERE IDADE = 101;

    EXCEPTIONWHEN NO_DATA_FOUND THEN

    DBMS_OUTPUT.PUT_LINE('Non atopei persoas de 101 anos');WHEN OTHERS THEN

    DBMS_OUTPUT.PUT_LINE('Erro: '||SQLCODE);DBMS_OUTPUT.PUT_LINE('Mensaxe: '||SQLERRM);

    END;/Non atopei persoas de 101 anosProcedimiento PL/SQL terminado correctamente.

    Oracle PL/SQL [Curso 2011-2012] 38 / 50

  • Excepcións (iv)

    Exemplo

    Crea un bloque de código que trate de insertar unha persoa, dexeito que non dea erro se o identi�cador (clave primaria) existe (porsuposto, non se fai a inserción)

    BEGININSERT INTO PERSOA(IDPERSOA, NOME, IDADE)

    VALUES('1','persoa que existe',1);EXCEPTION

    WHEN DUP_VAL_ON_INDEX THEN NULL;END;

    -- Exercicio: comprobar sen usar DUP_VAL_ON_INDEX

    Oracle PL/SQL [Curso 2011-2012] 39 / 50

    Excepcións (v)

    Uso de nomes de excepcións para SQLCODE especí�cos

    I Se sabemos o SQLCODE que da un erro, podemos asocialo a un nome de excepciónI Poderemos usalo en cláusulas WHEN da parte EXCEPTION.

    DECLAREvalor_demasiado_grande EXCEPTION;PRAGMA EXCEPTION_INIT(valor_demasiado_grande, -1438);

    BEGININSERT INTO PERSOA VALUES(77,'Matusalén Sr.', 1245);

    EXCEPTIONWHEN valor_demasiado_grande THEN

    DBMS_OUTPUT.PUT_LINE('Non se insertou a persoa' ||porque un valor excede o tamaño do campo.');

    DBMS_OUTPUT.PUT_LINE('Erro de Oracle: '||SQLERRM);END;/

    Non se insertou a persoa porque un valor excede o tamaño do campo.Erro de Oracle: ORA-01438: valor mayor que el que permite la precisiónespecificada para esta columna

    Procedimiento PL/SQL terminado correctamente.

    Oracle PL/SQL [Curso 2011-2012] 40 / 50

  • Excepcións (vi)

    Excepcións de usuario

    Hai 2 formas:

    I Excepcións con nomeI Declarar variable de tipo EXCEPTIONI Elevar (RAISE) a excepción baixo certas condiciónsI Pode ser capturada con WHEN THEN

    I Excepción sen nome, con SQLCODE e SQLERRI RAISE_APPLICATION_ERROR(,)I O debe ser negativo, menor que -20000

    Oracle PL/SQL [Curso 2011-2012] 41 / 50

    Excepcións (vii)

    Excepcións de usuario: exemplos

    Amosar o número de xubilados. Se hai máis de 100, elevar unhaexcepción e xestionala indicando que hai demasiados.

    DECLAREdemasiados_xubilados EXCEPTION;numero NUMBER;

    BEGINSELECT COUNT(*)

    INTO numeroFROM persoaWHERE idade>65;

    IF numero > 100 THENRAISE demasiados_xubilados;

    END IF;DBMS_OUTPUT.PUT_LINE('Hai '||numero||' xubilados.');

    EXCEPTIONWHEN demasiados_xubilados THENDBMS_OUTPUT.PUT_LINE('Hai demasiados xubilados');

    END;

    Oracle PL/SQL [Curso 2011-2012] 42 / 50

  • Excepcións (viii)

    Excepcións de usuario: exemplos

    Engade unha persoa. Se a idade é negativa, xenera unha excepcióncon código -20001 e mensaxe 'Idade inválida'.Non xestiones esa excepción, para que a execución do bloque falle.

    DECLARErPersoa PERSOA%ROWTYPE;

    BEGINrPersoa.IDPERSOA := '32233223K';rPersoa.NOME := 'nome';rPersoa.IDADE := -1;

    IF rPersoa.IDADE < 0 THENRAISE_APPLICATION_ERROR(-20001,'Idade inválida');

    END IF;INSERT INTO PERSOA VALUES rPersoa;

    END;/

    ERROR en línea 1:ORA-20001: Idade inválidaORA-06512: en línea 9

    Oracle PL/SQL [Curso 2011-2012] 43 / 50

    Coleccións en PL/SQL

    Tipos de datos en Oracle

    I Tipos prede�nidosI Tipos estruturados (clases)I Rexistros (RECORD) PL/SQLI Coleccións: VARRAY, TABLE OF ...

    TABLE OF ...

    I �Táboas PL/SQL�, pero non son realmente táboasI Persistencia a nivel de sesiónI Non admite sentencias DDL nin DMLI Non hai transaccións

    I Son coleccións dispersas (sparse) en memoria

    Oracle PL/SQL [Curso 2011-2012] 44 / 50

  • Coleccións en PL/SQLTáboas PL/SQL

    Creación

    TYPE IS TABLE OF [NOT NULL][INDEX BY {BINARY_INTEGER|VARCHAR2()}];

    ;

    I Créase primeiro o tipo e logo as variablesI O pode ser un rexistro PL/SQL (a partir

    de PL/SQL 2.3)I Indexada por números ou por cadeas de caracteres

    I Especi�cando INDEX BY ...: array asociativoI Índice numérico/carácter (admite %type)I Créase a táboa baleira (pero inicializada)I Podemos asignar directamente novos elementos do array

    I Omitindo INDEX BY ...: crea unha �nested table�I Índice numéricoI Créase a táboa sen inicializarI Hai que xestionar a asignación de novos elementos (extender

    táboa)

    Oracle PL/SQL [Curso 2011-2012] 45 / 50

    Coleccións en PL/SQLTáboas PL/SQL

    Creación � Exemplos

    DECLARETYPE taboadenomes

    IS TABLE OF EMP.ENAME%TYPEINDEX BY BINARY_INTEGER;

    nomes_empregados taboadenomes;nomes_clientes taboadenomes;

    TYPE taboadeempsIS TABLE OF EMP%ROWTYPEINDEX BY BINARY_INTEGER;

    empregados taboadeemps;

    rex_emp EMP%ROWTYPE;un_nome_de_empregado EMP.ENAME%TYPE;

    ...

    Oracle PL/SQL [Curso 2011-2012] 46 / 50

  • Coleccións en PL/SQLTáboas PL/SQL

    Acceso � Exemplos

    ()

    nomes_clientes(23) := 'Cliente Novo';un_nome_de_empregado := nomes_clientes(23);

    un_nome_de_empregado := nomes_empregados(2); -- Excepción NO_DATA_FOUND: non hai o índice 2

    Se é unha táboa de rexistros, para acceder a un campo:

    ().

    empregados(1).ename := 'PEPE';empregados(1).empno := 2434;empregados(15).ename := 'ANA';empregados(17) := rex_emp;

    un_nome_de_empregado := empregados(1).ename;rex_emp := empregados(1); -- Funcionarex_emp := empregados(3); -- Excepción NO_DATA_FOUND: non hai o índice 3

    Oracle PL/SQL [Curso 2011-2012] 47 / 50

    Coleccións en PL/SQLTáboas PL/SQL

    Métodos aplicables (i)

    I Constructor: () ou(): Só para �nested tables�.

    I Navegación: First, Next(), Prior(),Last: Devolven un índice

    DECLARETYPE arrayasoc_char_t IS TABLE OF varchar2(100) index by varchar2(20);TYPE nestedtable_int_t IS TABLE OF varchar2(100);array_char arrayasoc_char_t; nt_int nestedtable_int_t;indice_char varchar2(20);

    beginDBMS_OUTPUT.PUT_LINE('=== ARRAY ASOCIATIVO (INDEX BY VARCHAR) ===');-- Non podo usar o constructor, non é nested tablearray_char('un'):= 'Primeiro';array_char('dous') := 'Segundo';array_char('tres') := 'Terceiro';indice_char := array_char.first;while indice_char is not null loop

    dbms_output.put_line('Índice: '||indice_char||', valor: '|| array_char(indice_char));indice_char := array_char.next(indice_char);

    end loop;DBMS_OUTPUT.PUT_LINE('=== NESTED TABLE (INDICE NUMÉRICO) ===');nt_int := nestedtable_int_t(101, 76, 22);for indice_int in nt_int.first .. nt_int.last loop -- Só podo usar FOR se non hai ocos!

    dbms_output.put_line('Índice: '||indice_int||', valor: '|| nt_int(indice_int));end loop;

    end;/=== ARRAY ASOCIATIVO (INDEX BY VARCHAR) === === NESTED TABLE (INDICE NUM??RICO) ===Índice: dous, valor: Segundo Índice: 1, valor: 101Índice: tres, valor: Terceiro Índice: 2, valor: 76Índice: un, valor: Primeiro Índice: 3, valor: 22

    Oracle PL/SQL [Curso 2011-2012] 48 / 50

  • Coleccións en PL/SQLTáboas PL/SQL

    Métodos aplicables (ii)

    I Consulta: Count, Exists()I Borrado: Delete, Delete(),

    Delete(,)I Outros: Extend: só para �nested tables�, engade un elemento

    ó �nal da táboa.

    DECLARETYPE nestedtable_int_t IS TABLE OF varchar2(100);nt_int nestedtable_int_t;

    beginnt_int := nestedtable_int_t(101, 76, 22);dbms_output.put_line('Hai '||nt_int.count||' elementos');nt_int.extend;nt_int(nt_int.last) := 666;dbms_output.put_line('Hai '||nt_int.count||' elementos');if nt_int.exists(4) then

    dbms_output.put_line('O elemento 4 existe, e vale '||nt_int(4));end if;for indice_int in nt_int.first .. nt_int.last loop

    dbms_output.put_line('Índice: '||indice_int||', valor: '|| nt_int(indice_int));end loop;nt_int.delete(1);nt_int.delete(2,3);dbms_output.put_line('Hai '||nt_int.count||' elementos');nt_int.delete;dbms_output.put_line('Hai '||nt_int.count||' elementos');

    end;/Hai 3 elementosHai 4 elementosO elemento 4 existe, e vale 666Índice: 1, valor: 101Índice: 2, valor: 76Índice: 3, valor: 22Índice: 4, valor: 666Hai 1 elementosHai 0 elementos

    Oracle PL/SQL [Curso 2011-2012] 49 / 50

    Coleccións en PL/SQLTáboas PL/SQL

    BULK COLLECT

    I Permite cargar unha táboa PL/SQL directamente a partirdunha consulta

    I Sintaxe:select BULK COLLECT INTO FROM ...

    DECLARETYPE taboa_emps_t IS TABLE OF EMP%ROWTYPE;taboa_emps taboa_emps_t := taboa_emps_t();

    beginSELECT *

    BULK COLLECTINTO taboa_empsFROM EMP;

    DBMS_OUTPUT.PUT_LINE('Cargados '|| taboa_emps.count||' empregados na táboa PL/SQL.');

    end;/Cargados 14 empregados na táboa PL/SQL.

    Oracle PL/SQL [Curso 2011-2012] 50 / 50

  • PL/SQL: Procedementos, funcións e paquetes

    Miguel Rodríguez Penabad

    Laboratorio de Bases de Datos

    Universidade da Coruña

    Curso 2011-2012

    Bloques de código en PL/SQL

    I Bloques anónimos(xa vistos no punto anterior)

    I Rutinas almacenadas(SQL/PSM: Persistent Stored Modules en SQL:2003)

    I Procedementos (PROCEDURE)

    Non devolve ningún valor

    I Funcións (FUNCTION)

    Devolve un valor

    I Paquetes (PACKAGE)

    Permite construir bibliotecas de procedementos e funcións

    I Información no catálogo de Oracle, nas vistas (entre outras)I USER_OBJECTS (OBJ), ALL_OBJECTSI USER_PROCEDURES, ALL_PROCEDURESI USER_SOURCE, ALL_SOURCEI Orde de SQL*Plus DESC[RIBE]

    PL/SQL: Procedementos, funcións e paquetes [Curso 2011-2012] 2 / 10

  • Procedementos

    Creación

    CREATE [OR REPLACE]

    PROCEDURE ()

    [AUTHID {DEFINER|CURRENT_USER}]

    {IS|AS}

    I Parámetros: []

    I A especi�cación de entrada/saída pode serIN (predeterminado), OUT, IN OUT

    I O tipo dos argumentos pode serI Xenérico (p.ex. CHAR, non CHAR(10))I Derivado dun atributo dunha táboa (p.ex.

    PERSOA.IDPERSOA%TYPE)

    I Authid: ¾Con que permisos se executa?DEFINER: cos permisos do creador (predeterminado)CURRENT_USER: cos permisos do usuario que o executa

    I Bloque PL/SQL:I Non inclúe a palabra DECLARE antes da declaración de variablesI Acaba en END; ou en END ;

    PL/SQL: Procedementos, funcións e paquetes [Curso 2011-2012] 3 / 10

    Procedementos (ii)

    Exemplo

    Engadir unha persoa da que sabemos o identi�cador, nome e datade nacemento.

    CREATE OR REPLACE

    PROCEDURE InsPersoa(oID IN PERSOA.IDPERSOA%TYPE,

    oNome IN PERSOA.NOME%TYPE,

    DataNac DATE)

    IS

    aIdade NUMBER(3);

    BEGIN

    aIdade := MONTHS_BETWEEN(SYSDATE,DataNac)/12;

    INSERT INTO PERSOA (IDPERSOA,NOME,IDADE)

    VALUES(oID,oNome,aIdade);

    END InsPersoa;

    /

    Procedimiento creado.

    PL/SQL: Procedementos, funcións e paquetes [Curso 2011-2012] 4 / 10

  • Procedementos (iii)

    Creación, borrado, e uso

    I Se Oracle nos di "Creado con errores de compilación"SHOW ERRORS, ouSHOW ERRORS PROCEDURE

    I Para borrar o procedemento:DROP PROCEDURE

    I Para executar directamente o procedemento:I Nun bloque PL/SQL, chámase directamente.I Desde SQL*Plus (�modo SQL�, non PL/SQL)

    I CALL InsPersoa('5','2','21/12/1900');I EXEC[UTE] InsPersoa('5','2','21/12/1900');

    que se expande a

    BEGIN

    InsPersoa('5','2','21/12/1900');

    END;

    PL/SQL: Procedementos, funcións e paquetes [Curso 2011-2012] 5 / 10

    Procedementos (iv)

    Valores predeterminados e chamadas

    CREATE OR REPLACE PROCEDURE pordefecto(v1 int default 3, v2 int default 4)

    IS BEGIN

    dbms_output.put_line('v1: '||v1||', v2: '||v2);

    END;

    I Os argumentos con valores predeterminados ó �nal.I Omitindo un parámetro na chamada, o argumento toma o

    valor predeterminado.

    begin

    pordefecto(1,2); pordefecto(1); pordefecto();

    end;

    /

    v1: 1, v2: 2

    v1: 1, v2: 4

    v1: 3, v2: 4

    I Novidade Oracle 11g: indicar os nomes dos argumentos nachamada (pre 11: só en packages)

    I Calquera argumento pode ter valor predeterminadoI Reordenamos/omitimos parámetros

    begin

    pordefecto(v2 => 77, v1 => 10); pordefecto(v2 => 77);

    end;

    /

    v1: 10, v2: 77

    v1: 3, v2: 77

    PL/SQL: Procedementos, funcións e paquetes [Curso 2011-2012] 6 / 10

  • Funcións

    Creación

    CREATE [OR REPLACE]

    FUNCTION ()

    RETURN

    [AUTHID {DEFINER|CURRENT_USER}]

    {IS|AS}

    I Debemos indicar o tipo (xenérico) que devolveI Bloque PL/SQL: Debe incluir RETURN I Se a creamos con erros de compilación:

    I SHOW ERRORS, ou SHOW ERRORS FUNCTION

    I Para borrar unha función: DROP FUNCTION I Para executar:

    I Non se pode directamente como os procedementosI En sentencias SELECT

    (Excepto cando a función usa DML que modi�ca datos)

    I En PL/SQL (variable := funcion(argumentos), ...)

    PL/SQL: Procedementos, funcións e paquetes [Curso 2011-2012] 7 / 10

    Funcións (ii)

    Exemplo

    Función que calcula o ano de nacemento (aprox.) a partir da idade.

    CREATE OR REPLACE FUNCTION AnoNac(idade IN PERSOA.IDADE%TYPE)

    RETURN NUMBER

    IS

    BEGIN

    RETURN to_number(to_char(sysdate,'yyyy')) - idade;

    END AnoNac;

    /

    Función creada.

    SELECT Idade, AnoNac(Idade)

    FROM PERSOA;

    IDADE ANONAC(IDADE)

    ---------- -------------

    51 1957

    851 1157

    17 1991

    PL/SQL: Procedementos, funcións e paquetes [Curso 2011-2012] 8 / 10

  • Funcións (iii)

    Uso en vistas

    CREATE VIEW V_PERSOA

    AS SELECT IDPERSOA, NOME, AnoNac(IDADE) ANO_NAC

    FROM PERSOA;

    Uso en condicionais

    I Función que modi�ca datosI Devolve BOOLEAN

    true se foi posiblefalse se non o foi

    PL/SQL: Procedementos, funcións e paquetes [Curso 2011-2012] 9 / 10

    Paquetes

    De�nición da interfaz

    CREATE [OR REPLACE] PACKAGE {IS|AS}

    -- Declaración de variables globais do paquete

    ...

    -- Prototipos de procedementos e funcións

    PROCEDURE (

  • Triggers en Oracle

    Miguel Rodríguez Penabad

    Laboratorio de Bases de Datos

    Universidade da Coruña

    Curso 2012-2013

    Creación de triggers

    I Para crear ou modi�car:

    CREATE [OR REPLACE] TRIGGER {BEFORE|AFTER|INSTEAD OF}

    [OR ...] ON [ REFERENCING [NEW AS tupla_nova]

    [OLD AS tupla]_vella] ][FOR EACH ROW][WHEN ()]

    { | CALL }

    I Para borrar:

    DROP TRIGGER

    I Para activar ou desactivar:I Triggers individuais

    ALTER TRIGGER nome_trigger> {ENABLE|DISABLE}

    I Todos os triggers asociados a unha táboa

    ALTER TABLE {ENABLE|DISABLE} ALL TRIGGERS

    Triggers en Oracle [Curso 2012-2013] 2 / 21

  • Creación de triggers

    I Para consultar os existentes:Táboas ALL_TRIGGERS, USER_TRIGGERS

    SQL> desc user_triggersNombre ¾Nulo? Tipo

    ----------------------------- -------- -------------

    TRIGGER_NAME VARCHAR2(30)

    TRIGGER_TYPE VARCHAR2(16)

    TRIGGERING_EVENT VARCHAR2(227)

    TABLE_OWNER VARCHAR2(30)

    BASE_OBJECT_TYPE VARCHAR2(16)

    TABLE_NAME VARCHAR2(30)

    COLUMN_NAME VARCHAR2(4000)

    REFERENCING_NAMES VARCHAR2(128)

    WHEN_CLAUSE VARCHAR2(4000)

    STATUS VARCHAR2(8)

    DESCRIPTION VARCHAR2(4000)

    ACTION_TYPE VARCHAR2(11)

    TRIGGER_BODY LONG

    select trigger_name, trigger_type, triggering_event, when_clause, statusfrom user_triggerswhere table_name='EMP2';

    TRIGGER_NAME TRIGGER_TYPE TRIGGER WHEN_CLAUSE STATUS

    ---------------- --------------- ------- --------------- --------

    T_EMP_SALPOS_BUR BEFORE EACH ROW UPDATE nova.SAL

  • Comparación co estándar SQL:2003 (cont.)

    Condición

    I Podemos usar as variables de transición NEW e OLDI Non admite sentencias SELECTI Solución:

    I Omitir condición (omitir cláusula WHEN)I Incluir a condición nun IF na acción

    Acción

    I Bloque PL/SQL (ou chamada a un procedemento).I Non se permiten sentencias de control transaccional (COMMIT,

    ROLLBACK, ...)I As variables de transición son consideradas bind variables

    Irán precedidas do símbolo dous puntos(:NEW e :OLD se non usamos nomes alternativos)

    I De�nidas para todos os tipos de trigger a nivel de �laI NEW contén o valor novo, e OLD o antigo

    I En triggers de inserción, OLD será NULLI En triggers de borrado, NEW será NULL

    I Coñécese o valor nos triggers BEFORE e AFTERI OLD nunca pode ser modi�cadoI NEW pode modi�carse nos triggers BEFORE

    Triggers en Oracle [Curso 2012-2013] 5 / 21

    Exemplos

    Táboas de referencia

    CREATE TABLE PRODUTO(CODPROD NUMERIC(3) NOT NULL,NOMPROD VARCHAR(40),PREZO NUMERIC(6,2),CONSTRAINT PK_PRODUTO PRIMARY KEY(CODPROD) );

    CREATE TABLE FACTURA(NUMERO CHAR(5),DATA DATE DEFAULT SYSDATE,TOTAL NUMERIC(7,2),CONSTRAINT PK_FACTURA PRIMARY KEY(NUMERO) );

    CREATE TABLE LINA(NUMFAC CHAR(5),NUMLI NUMERIC(3),CODPROD NUMERIC(3),CANTIDADE NUMERIC(2),PREZO NUMERIC(6,2),SUBTOTAL NUMERIC(7,2),CONSTRAINT PK_LINA PRIMARY KEY(NUMFAC,NUMLI),CONSTRAINT FK_FAC FOREIGN KEY(NUMFAC)

    REFERENCES FACTURA(NUMERO) ON DELETE CASCADE,CONSTRAINT FK_PROD FOREIGN KEY(CODPROD)

    REFERENCES PRODUTO(CODPROD) ON DELETE CASCADE );

    Triggers en Oracle [Curso 2012-2013] 6 / 21

  • Exemplos

    Actualización de prezos

    Non se permite que unha actualización asigne un prezo negativo aun produto.

    CREATE OR REPLACE TRIGGER t_prod_prezopos_burBEFORE UPDATE ON PRODUTOREFERENCING NEW AS tupla_nova OLD AS tupla_vellaFOR EACH ROWWHEN (tupla_nova.Prezo < 0)

    BEGIN:tupla_nova.Prezo := :tupla_vella.Prezo;

    END;

    SQL> SELECT * FROM PRODUTO;

    CODIGO NOME PREZO

    ---------- -------------------- ----------

    1 TORNILLO ,23

    SQL> UPDATE PRODUTO SET PREZO=-2;

    1 fila actualizada.

    SQL> SELECT * FROM PRODUTO;

    CODIGO NOME PREZO

    ---------- -------------------- ----------

    1 TORNILLO ,23

    Triggers en Oracle [Curso 2012-2013] 7 / 21

    Exemplos

    Actualización de prezos + inserción

    Non se permite que unha actualización asigne un prezo negativo aun produto. Tampouco permitimos a inserción de prezos negativos.Se se intenta, o prezo queda en 0.

    -- drop trigger t_prod_prezopos_bur;

    CREATE OR REPLACE TRIGGER t_prod_prezopos_buirBEFORE UPDATE OR INSERT ON PRODUTOREFERENCING NEW AS tupla_nova OLD AS tupla_vellaFOR EACH ROWWHEN (tupla_nova.Prezo < 0)

    BEGINIF UPDATING

    THEN :tupla_nova.Prezo := :tupla_vella.Prezo;ELSE :tupla_nova.Prezo := 0;

    END IF;END;

    SQL> INSERT INTO PRODUTO VALUES(2, 'TUERCA', -2);

    1 fila creada.

    SQL> select * from produto;

    CODIGO NOME PREZO

    ---------- -------------------- ----------

    1 TORNILLO ,23

    2 TUERCA 0

    2 filas seleccionadas.

    Triggers en Oracle [Curso 2012-2013] 8 / 21

  • ExemplosProblemas: táboas mutantes

    Actualización de prezos (evitar inserción)

    Non se permite unha inserción de produtos con prezos negativos.Se se intenta, a �la non se insertará.

    CREATE OR REPLACE TRIGGER t_prod_prezopos_falla_airAFTER INSERT ON PRODUTOFOR EACH ROWWHEN (new.Prezo < 0)

    BEGINDELETE FROM PRODUTO WHERE codprod = :new.codprod;

    END;

    O trigger créase correctamente, pero:

    SQL> insert into produto values(4,'Erro', -10);

    insert into produto values(4,'Erro', -10)

    *

    ERROR en línea 1:

    ORA-04091: la tabla MIGUEL.PRODUTO está mutando, puede que el

    disparador/la función no puedan verla

    ORA-06512: en "MIGUEL.T_PROD_PREZOPOS_FALLA_AIR", línea 1

    ORA-04088: error durante la ejecución del disparador

    'MIGUEL.T_PROD_PEZOPOS_FALLA_AIR'

    Triggers en Oracle [Curso 2012-2013] 9 / 21

    O problema das táboas mutantes

    Unha táboa está mutandoI Está modi�cándose actualmente por unha sentencia DML

    I Triggers a nivel �la (FOR EACH ROW)I Excepto BEFORE INSERT en insercións de 1 �la

    I Está léndose ou modi�cándos para garantir integridadereferencial.

    I Non podemos acceder mediante SQL (SELECT, INSERT,DELETE, UPDATE) á táboa mutante.

    Exemplos de triggers que fallan

    I t_prod_prezopos_falla_air (transparencia anterior):PRODUTO está sendo modi�cado

    I trigger_falla1: LINA está mutando (por FK_PRODUTO)I trigger_falla2: PRODUTO está mutando (por FK_PRODUTO)

    CREATE TRIGGER trigger_falla1AFTER DELETE ON PRODUTO

    FOR EACH ROWDECLARE N NUMBER;BEGINSELECT COUNT(*) INTO N

    FROM LINA;END;-- O seguinte DELETE falladelete from produto where codigo=1;

    CREATE TRIGGER trigger_falla2AFTER DELETE ON LINAFOR EACH ROWDECLARE N NUMBER;BEGINSELECT COUNT(*) INTO N

    FROM PRODUTO;END;-- O seguinte DELETE falladelete from produto where codigo=1;

    Triggers en Oracle [Curso 2012-2013] 10 / 21

  • O problema das táboas mutantesPosibles solucións

    1. Cambiar a acción (se é posbile)2. Convertir en trigger a nivel de sentencia3. Simular táboas de transición

    (combinación de varios triggers e procedementos en packages)

    Opción 1: Cambiar acción

    CREATE OR REPLACE TRIGGER t_prod_prezopos2_airAFTER INSERT ON PRODUTOFOR EACH ROWWHEN (new.Prezo < 0)

    BEGINRAISE_APPLICATION_ERROR(-20002, 'Prezo inválido');

    END;

    SQL> insert into produto values(22,'non se inserta', -2);

    insert into produto values(22,'non se inserta', -2)

    *

    ERROR en línea 1:

    ORA-20002: Prezo inválido

    ORA-06512: en "MIGUEL.T_PROD_PREZOPOS2_AIR", línea 1

    ORA-04088: error durante la ejecución del disparador

    'MIGUEL.T_PROD_PREZOPOS2_AIR'

    Triggers en Oracle [Curso 2012-2013] 11 / 21

    O problema das táboas mutantesPosibles solucións (cont.)

    Opción 2: Convertir a trigger de sentencia

    I A condición pode variarI O trigger pode non ser totalmente equivalente

    ¾Que pasa se había prezos negativos anteriores?

    CREATE OR REPLACE TRIGGER t_prod_prezopos3_aisAFTER INSERT ON PRODUTOBEGIN

    DELETE FROM PRODUTOWHERE Prezo select count(*) from produto;

    COUNT(*)

    ----------

    2

    SQL> INSERT INTO PRODUTO VALUES(3,'NON SE INSERTA',-2);

    1 fila creada.

    SQL> select count(*) from produto;

    COUNT(*)

    ----------

    2

    Triggers en Oracle [Curso 2012-2013] 12 / 21

  • O problema das táboas mutantesPosibles solucións (cont.)

    Opción 3: Simular táboas de transición

    I Trigger de �la (FOR EACH ROW)I BEFORE ou AFTER I Almacena as �las nunha táboa temporal

    I Trigger de sentenciaI AFTER I Recorre a táboa temporal procesando as �lasI Remata borrando a táboa temporal

    I Uso de packageI Para declarar a táboa temporalI Procedementos almacenar e recorrer as �las

    1. Crear paqueteCREATE OR REPLACE PACKAGE EVITAR_MUTANTESASPROCEDURE proc_ins_row(cod IN PRODUTO.Codprod%TYPE,

    pre IN PRODUTO.Prezo%TYPE);PROCEDURE proc_after_insert;END EVITAR_MUTANTES;

    Triggers en Oracle [Curso 2012-2013] 13 / 21

    O problema das táboas mutantesPosibles solucións (cont.)

    Opción 3: Simular táboas de transición (cont.)

    2. Crear corpo do paqueteCREATE OR REPLACE PACKAGE BODY EVITAR_MUTANTES AS

    TYPE rex_prod IS RECORD(codigo PRODUTO.Codprod%TYPE,prezo PRODUTO.Prezo%TYPE );

    TYPE tab_prod_t IS TABLE OF rex_prod;tab_prod tab_prod_t := tab_prod_t();

    PROCEDURE proc_ins_row(cod IN PRODUTO.Codprod%TYPE,pre IN PRODUTO.Prezo%TYPE) IS BEGIN

    tab_prod.extend;tab_prod(tab_prod.last).codprod := cod;tab_prod(tab_prod.last).prezo := pre;

    END proc_ins_row;

    PROCEDURE proc_after_insert IS BEGINFOR i in tab_prod.first .. tab_prod.last LOOP

    DELETE FROM PRODUTO WHERE CODPROD = tab_prod(i).codprod;END LOOP;tab_prod.delete;

    END;END EVITAR_MUTANTES;

    Triggers en Oracle [Curso 2012-2013] 14 / 21

  • O problema das táboas mutantesPosibles solucións (cont.)

    Opción 3: Simular táboas de transición (cont.)

    3. Crear os triggersCREATE OR REPLACE TRIGGER t_prod_prezopos_birBEFORE INSERT ON PRODUTOFOR EACH ROWWHEN (new.Prezo < 0)CALL EVITAR_MUTANTES.PROC_INS_ROW(:new.codprod,:new.Prezo)/CREATE OR REPLACE TRIGGER t_prod_prezopos_aisAFTER INSERT ON PRODUTOCALL EVITAR_MUTANTES.PROC_AFTER_INSERT/SQL> ALTER TABLE PRODUTO DISABLE ALL TRIGGERS;

    SQL> INSERT INTO PRODUTO VALUES(3,'INSERTADO',-2);

    1 fila creada.

    SQL> SELECT * FROM PRODUTO;

    CODPROD NOMPROD PREZO

    ---------- -------------------- ----------

    3 INSERTADO -2

    SQL> ALTER TABLE PRODUTO ENABLE ALL TRIGGERS;

    SQL> INSERT INTO PRODUTO VALUES(4, 'NON INSERTADO', -4);

    1 fila creada.

    SQL> SELECT * FROM PRODUTO;

    CODPROD NOMPROD PREZO

    ---------- -------------------- ----------

    3 INSERTADO -2

    Triggers en Oracle [Curso 2012-2013] 15 / 21

    Triggers INSTEAD OF

    I Non existían no SQL:2003. Foron incluídos no estándarSQL:2008

    I Só poden de�nirse sobre vistasI Usados para poder actualizar vistas non actualizables.I Sempre son a nivel de �la.

    Exemplo

    CREATE TABLE EMP2AS SELECT * FROM EMP;

    CREATE VIEW EMPS_POR_DEPAS SELECT D.DEPTNO, DNAME, LOC, COUNT(*) EMPS

    FROM EMP2 E JOIN DEPT D ON E.DEPTNO=D.DEPTNOGROUP BY D.DEPTNO, DNAME, LOC;

    SQL> SELECT * FROM EMPS_POR_DEP;

    DEPTNO DNAME LOC EMPS

    ------ -------------- ------------- ----------

    20 RESEARCH DALLAS 5

    10 ACCOUNTING NEW YORK 3

    30 SALES CHICAGO 6

    Triggers en Oracle [Curso 2012-2013] 16 / 21

  • Triggers INSTEAD OF (cont.)

    Exemplo (cont.)

    I A vista non é actualizable.SQL> DELETE FROM EMPS_POR_DEP WHERE DNAME='SALES';

    DELETE FROM EMPS_POR_DEP WHERE DNAME='SALES'

    *

    ERROR en línea 1:

    ORA-01732: operación de manipulación de datos no válida en esta vista

    I Podemos de�nir a �regra de actualización� co trigger.CREATE OR REPLACE TRIGGER T_EMPS_POR_DEP_BORRAEMPS_IDRINSTEAD OF DELETE ON EMPS_POR_DEPFOR EACH ROWBEGIN

    DELETE FROM EMP2WHERE DEPTNO = :OLD.DEPTNO;

    END;SQL> DELETE FROM EMPS_POR_DEP WHERE DNAME='SALES';

    1 fila suprimida.

    Triggers en Oracle [Curso 2012-2013] 17 / 21

    Triggers non estándarEventos DDL e de base de datos

    I Eventos que non controlan modi�cación de datosI Usados en auditoría de base de datos

    CREATE TABLE LOGCREACIONS(USUARIO CHAR(20), DATA DATE,TIPO_OBX CHAR(20), OBXECTO CHAR(20));

    CREATE OR REPLACE TRIGGER t_schema_LogCreacions_acsAFTER CREATE ON SCHEMA

    BEGININSERT INTO LogCreacions(usuario,data,tipo_obx,obxecto)VALUES(USER,SYSDATE,ORA_DICT_OBJ_TYPE,ORA_DICT_OBJ_NAME);

    END LogCreacions;/

    CREATE TABLE CONEXIONS(USUARIO CHAR(20), DATA_HORA DATE, TIPO CHAR(12));

    CREATE TRIGGER LOG_CONEXIONAFTER LOGON ON DATABASEBEGININSERT INTO CONEXIONS(USUARIO,DATA_HORA,TIPO)

    VALUES (USER, SYSDATE, 'CONEXIÓN');END LOG_CONEXION;

    /

    Triggers en Oracle [Curso 2012-2013] 18 / 21

  • Novidades Oracle 11gOrdenación da execución de triggers

    I Útil se existen varios triggers para o mesmoevento/granularidade/tempo activación

    I Cláusula FOLLOWS

    CREATE OR REPLACE TRIGGER trigger_1AFTER UPDATE ON tFOR EACH ROWBEGIN...

    END trigger_1;

    -- O trigger_2 executarase despois do trigger_1,-- despois de actualizar cada fila de tCREATE OR REPLACE TRIGGER trigger_2AFTER UPDATE ON tFOR EACH ROWFOLLOWS trigger_1BEGIN...

    END trigger_2;

    Triggers en Oracle [Curso 2012-2013] 19 / 21

    Novidades Oracle 11gTriggers compostos (compound triggers)

    I Permite establecer varias acciónsI Con distinta granularidadeI Con distinto tempo de activación

    CREATE OR REPLACE TRIGGER trigger_compFOR ON COMPOUND TRIGGER

    -- Non é necesario incluir os seguintes catro bloques

    BEFORE STATEMENT ISBEGIN END BEFORE STATEMENT;

    BEFORE EACH ROW ISBEGIN END BEFORE EACH ROW;

    AFTER EACH ROW ISBEGIN END AFTER EACH ROW;

    AFTER STATEMENT ISBEGIN END AFTER STATEMENT;

    END trigger_comp;

    Triggers en Oracle [Curso 2012-2013] 20 / 21

  • Novidades Oracle 11gTriggers compostos (compound triggers) - exemplo

    -- Similar ó exemplo que usa un packageCREATE OR REPLACE TRIGGER t_prod_comp

    FOR INSERT ON PRODUTOCOMPOUND TRIGGER

    TYPE tab_prod_t IS TABLE OF PRODUTO.Codprod%TYPE;tab_prod tab_prod_t := tab_prod_t();

    BEFORE EACH ROW ISBEGIN

    IF :new.prezo