PL/SQL Francisco Moreno Universidad Nacional. Paquetes: sobrecarga de subprogramas obrecarga de...

Post on 28-Jan-2016

225 views 0 download

Transcript of PL/SQL Francisco Moreno Universidad Nacional. Paquetes: sobrecarga de subprogramas obrecarga de...

PL/SQLPL/SQLFrancisco Moreno

Universidad Nacional

Paquetes: sobrecarga de subprogramas

• Sobrecarga de subprogramas:obrecarga de subprogramas: Varios subprogramas pueden tener el mismo nombre en un paquetepaquete siempre y cuando no haya ambigüedad con los tipos de los parámetros y de retorno

• No puede haber sobrecarga si la diferencia entre los subprogramas con el mismo nombre solo se basa en:– el modo (IN, OUT)

– el tipo de retorno de una función

– Tipos de datos de la misma familia como CHAR y VARCHAR*

* Se permite para ciertos tipos de datos, por ejemplo NUMBER vs POSITIVE, aunque el comportamiento es bastante peculiar.

No puede haber sobrecarga entre:

PROCEDURE xxx(a VARCHAR)

PROCEDURE yyy(a IN NUMBER)

FUNCTION zzz(a IN NUMBER)

RETURN NUMBER

PROCEDURE xxx(b CHAR)

PROCEDURE yyy(b OUT NUMBER)

FUNCTION zzz(b IN NUMBER)

RETURN VARCHAR

Ejemplo

CREATE OR REPLACE PACKAGE mat ISFUNCTION area(radio NUMBER) RETURN NUMBER;FUNCTION area(largo NUMBER, ancho NUMBER) RETURN NUMBER;END;/

CREATE OR REPLACE PACKAGE BODY mat IS FUNCTION area(radio NUMBER) RETURN NUMBER IS BEGIN RETURN 3.1416*radio*radio; END; FUNCTION area(largo NUMBER, ancho NUMBER) RETURN NUMBER IS BEGIN RETURN largo*ancho; END;END;/

Para ejecutar:

BEGINDBMS_OUTPUT.PUT_LINE(mat.area(10));DBMS_OUTPUT.PUT_LINE(mat.area(20,30

));END;/

SELECT mat.area(10), mat.area(20,30) FROM dual;

Variables en los Paquetes

• Las variables de los paquetes son persistentes durante la sesión• Las variables declaradas en la especificación son

públicas: se pueden acceder y modificar directamente desde otros subprogramas o bloques anónimos

• Las variables declaradas en el cuerpo son privadas: solo se pueden acceder dentro del cuerpo. Si se desea ver su valor desde afuera del cuerpo, se

debe crear un subprograma (público) a través del cual se acceden (cf. encapsulamiento)

CREATE OR REPLACE PACKAGE vbles IS nro_veces NUMBER(3) := 0; -- Vble

pública PROCEDURE incrementa_veces; PROCEDURE muestra_veces; PROCEDURE muestra_impresiones; END; /

Ejemplo

Subprogramas públicos

Este procedimiento no es estrictamente necesario aunque es recomendable ¿por qué?

CREATE OR REPLACE PACKAGE BODY vbles IS nro_impresiones NUMBER(3) := 0; --Variable privada PROCEDURE incrementa_veces IS BEGIN nro_veces := nro_veces + 1; END; PROCEDURE muestra_veces IS BEGIN DBMS_OUTPUT.PUT_LINE(nro_veces); nro_impresiones := nro_impresiones + 1; END;

PROCEDURE muestra_impresiones IS BEGIN DBMS_OUTPUT.PUT_LINE(nro_impresiones); END;END;/

Para ejecutar:

• EXECUTE vbles.muestra_veces; • EXECUTE vbles.muestra_impresiones;• EXECUTE vbles.incrementa_veces;• EXECUTE vbles.muestra_veces;• EXECUTE vbles.muestra_veces; • EXECUTE vbles.muestra_impresiones;

0 1

1 1

3

Directamente se pueden acceder las variables públicas pero NO las privadas. Igual sucede cuando hay subprogramas privados. Por ejemplo:

BEGIN vbles.nro_veces := vbles.nro_veces + 1;END;/

BEGIN DBMS_OUTPUT.PUT_LINE(vbles.nro_veces);END;/

BEGIN DBMS_OUTPUT.PUT_LINE(vbles.nro_impresiones);END;/ Genera error porque nro_impresiones NO es pública

Cursores REF

• Los cursores REF permiten pasar los resultados de una consulta a otro subprograma

• El programa que recibe el cursor REF lo puede recorrer como un cursor normal

• Hay cursores REF fuertes (se les especifica el tipo de retorno) y débiles.

Ejemplo de cursor REF fuerte

--Sea la tabla:DROP TABLE emp;CREATE TABLE emp( cedula NUMBER(8) PRIMARY KEY, nom VARCHAR2(10) NOT NULL);

INSERT INTO emp VALUES(8,'Loui');INSERT INTO emp VALUES(7,'Dina');INSERT INTO emp VALUES(5,'Xena');

Se define el tipo del cursor REF y su tipo de retorno:

CREATE OR REPLACE PACKAGE prueba_ref_cursor

IS TYPE ref_emp_type IS REF CURSOR RETURN emp%ROWTYPE; END;/

Tipo de retorno del REF CURSOR: es lo que lo hace “fuerte”

CREATE OR REPLACE PROCEDURE devuelve_cursor

/*Se envía el cursor REF fuerte como parámetro de salida:*/

(c OUT prueba_ref_cursor.ref_emp_type)ASBEGIN OPEN c FOR SELECT * FROM emp; --Se

llenaEND;/

Ya el cursor REF c queda lleno (pero los datos no se recorren acá)

--Se invoca el procedimiento anterior y se imprimen--los resultados:DECLAREresultados prueba_ref_cursor.ref_emp_type;a emp%ROWTYPE;BEGINdevuelve_cursor(resultados); --Se invocaLOOP --Se recorre FETCH resultados INTO a; EXIT WHEN resultados%NOTFOUND; DBMS_OUTPUT.PUT_LINE(a.cedula || ' ' || a.nom);END LOOP;CLOSE resultados;END;/

Ejemplo de cursor REF débil: SYS_REFCURSOR

CREATE OR REPLACE PROCEDURE devuelve_cursor/*Se envía el cursor REF débil (parámetro de salida) y el

nombre de la tabla a consultar */ (c OUT SYS_REFCURSOR, nom_tabla IN VARCHAR)ASBEGIN OPEN c FOR 'SELECT * FROM ' || nom_tabla; --Se llenaEND;/

Nótese que la consulta asociada con un REF CURSOR puede ser ¡una cadena de caracteres!, esto permite crear cursores cuya consulta no se conoce en tiempo de compilación.

--Sea la tabla:

DROP TABLE cliente;CREATE TABLE cliente( ced NUMBER(8) PRIMARY KEY, nom VARCHAR2(10) NOT NULL, ciudad VARCHAR2(10) NOT NULL);

INSERT INTO cliente VALUES(1,'Pedra', 'Cali');INSERT INTO cliente VALUES(2,'Kathy',

'Londres');INSERT INTO cliente VALUES(3,'Donna', 'Paris');

DECLARE--Una variable de tipo SYS_REFCURSORmi_ref_cursor SYS_REFCURSOR;a1 emp%ROWTYPE; a2 cliente%ROWTYPE;BEGINdevuelve_cursor(mi_ref_cursor,'emp'); --Se invocaLOOP FETCH mi_ref_cursor INTO a1; EXIT WHEN mi_ref_cursor%NOTFOUND; DBMS_OUTPUT.PUT_LINE(a1.cedula || ' ' ||

a1.nom);END LOOP;CLOSE mi_ref_cursor; El programa

continua en la próxima

diapositiva

devuelve_cursor(mi_ref_cursor, 'cliente'); --Se invocaLOOP FETCH mi_ref_cursor INTO a2; EXIT WHEN mi_ref_cursor%NOTFOUND; DBMS_OUTPUT.PUT_LINE(a2.ced || ' ' || a2.nom || '

' || a2.ciudad);END LOOP;CLOSE mi_ref_cursor;END;/

¿Se podrá adaptar el programa anterior de tal forma que quede funcionando para imprimir los resultados de cualquier consulta? El problema es que solo en tiempo de ejecución se sabe cual es la tabla a consultar y sus columnas Solución: Usar el paquete DBMS_SQL.

DECLARErcursor SYS_REFCURSOR;theCursor INTEGER;colCnt NUMBER;dTbl DBMS_SQL.DESC_TAB;val VARCHAR2(100);BEGINOPEN rcursor FOR SELECT * FROM cliente;theCursor := DBMS_SQL.TO_CURSOR_NUMBER(rcursor);DBMS_SQL.DESCRIBE_COLUMNS(theCursor,colCnt,dTbl);FOR i IN 1 .. colCnt LOOP DBMS_SQL.DEFINE_COLUMN(theCursor,i,val,100);END LOOP;

Ensayar con emp

WHILE(DBMS_SQL.FETCH_ROWS(theCursor) > 0) LOOP FOR i IN 1 .. colCnt LOOP DBMS_SQL.COLUMN_VALUE(theCursor,i,val); DBMS_OUTPUT.PUT_LINE(dTbl(i).col_name || ': ' || val); END LOOP; DBMS_OUTPUT.PUT_LINE('Siguiente registro');END LOOP;DBMS_SQL.CLOSE_CURSOR(theCursor);END;/