Está en la página 1de 121

Programacin en Oracle con PL/SQL - 1 de 121

ProgramacinenOracleconPL/SQL
ApuntesdeClase

por:JosAndrsMartnezSilva

ElaboradosenFebreroMarzodel2009

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 2 de 121

Tabla de Contenidos
1. Revisin de Conceptos Bsicos de SQL................................................................................................3 1.1 Primer ejemplo de programacin con PL/SQL: Triggers..............................................................10 1.2 Consultas sobre la Informacin Almacenada:...............................................................................11 1.3 Vistas:............................................................................................................................................13 2. Introduccin a PL/SQL........................................................................................................................15 2.1 Variables de sustitucin:................................................................................................................17 2.2 Tipos de datos soportados en PL/SQL:.........................................................................................21 2.3 Bloques anidados y control de flujo:.............................................................................................22 2.4 Funciones: ....................................................................................................................................25 2.5 Adicin de Secuencias a las Tablas:..............................................................................................27 2.6 Commit, Rollback y Savepoint:....................................................................................................30 2.7 Revisin Parcial de lo visto hasta este punto:...............................................................................32 3. Procedimientos Almacenados: ............................................................................................................36 3.1 Bloques de cdigo anidados: ........................................................................................................37 4. Paquetes:..............................................................................................................................................38 4.1 Empleando CASE para controlar el flujo de un Programa: .........................................................46 4.2 CASE con condiciones de bsqueda: ...........................................................................................47 4.3 Control de Iteraciones con LOOP:................................................................................................48 4.3.1 Loop Simple:.........................................................................................................................48 4.3.2 WHILE LOOPS:....................................................................................................................50 4.3.3 FOR LOOP:...........................................................................................................................53 4.3.4 Loops Anidados:....................................................................................................................54 5. Parmetros de Entrada y Salida Aclaracin: ....................................................................................55 6. Manejo de Excepciones: .....................................................................................................................57 6.1 Excepciones Comunes Incluidas en Oracle:.................................................................................59 7. Cursores: .............................................................................................................................................62 7.1 Definicin de un CURSOR explcito: ..........................................................................................64 7.1.1 Records: ................................................................................................................................64 7.2 Atributos de los CURSORES: ......................................................................................................67 7.3 Cursor For Loop: ..........................................................................................................................71 7.4 Parmetros para los Cursores: ......................................................................................................71 7.5 Cursores anidados: .......................................................................................................................73 7.6 For Update: ...................................................................................................................................88 8. De vuelta a los Triggers: .....................................................................................................................91 8.1 PRAGMA AUTONOMOUS_TRANSACTION: .........................................................................95 8.2 INSTEAD OF Trigger: .................................................................................................................98 9. Colecciones: ........................................................................................................................................99 9.1 Tablas PL/SQL: ............................................................................................................................99 9.1.1 Tablas Index By.....................................................................................................................99 9.1.2 Tablas Anidadas:..................................................................................................................102 9.2 Mtodos de las Colecciones: ......................................................................................................103 9.3 Tablas de registros: .....................................................................................................................107
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 3 de 121

1.RevisindeConceptosBsicosdeSQL Llaveprimaria:elcampooconjuntodecamposqueidentificademaneraunvocaunregistro dentrodeunatabla Llavefornea:elcampooconjuntodecamposquepermiterelacionarunatablaconotra Indices:anlogoalndicedeldirectoriotelefnico,esuncriteriodeordenamientoque permiteencontrarmsrpidolainformacinquesebusca.Enpalabrasdelautor: Youneedtocarefullyanalyzehowdatawillbequeriedfromeachtable,andthencreatean appropriatesetofindexes.Youdontwanttoindexeverything,becausethatwould unnecessarilyslowdowntheprocessofinserting,updating,anddeletingdata.ThatswhyI saidcarefully. Restricciones:Constraints UKC:estableceuncampooconjuntodecamposquenopuedenrepetirsedentrodeuna tabla. PKC:estableceuncampooconjuntodecamposcomolallaveprimariadeunatabla. FKC:estableceuncampooconjuntodecamposcomolallaveforneadeunatabla. Ejercicio1: DadoelsiguientemodeloescribaelcdigoDDLqueseencargardecrearloenunabasede datosOracle

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 4 de 121

Elcono indicaquedichocampooconjuntodecamposdebendefinirsecomoFKC,el cono indicaquesetratadeuncampoquepuedetomarelvalorNULL,loscoloresindican loscamposqueseempleancomollavesforneas. SolucinPropuesta:


-- script de creacion de las tablas del modelo de la 2da clase del curso CREATE TABLE alumno( id_alumno number not null, documento_alumno varchar2(20) not null, nombre_alumno varchar2(50) not null, apellido_alumno varchar2(50) not null ); CREATE TABLE leccion( id_leccion number not null, fecha date not null ); CREATE TABLE examen( id_examen number not null, descripcion_examen varchar2(50) not null ); CREATE TABLE tema( id_tema number not null, nombre_tema varchar2(50) not null, descripcion_tema varchar2(200), id_leccion number not null ); CREATE TABLE examen_leccion( id_examen number not null, id_leccion number not null ); CREATE TABLE examen_alumno( id_examen number not null, id_alumno number not null, fecha date not null, calificacion number );

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 5 de 121

Ejercicio2: EscribaelcdigoDDLqueincluirlasrestriccionesnecesariasparadefinirlasllaves primariasyforneas SolucinPropuesta:


-- restricciones para cada una de las tablas --llaves primarias PKC -- ALTER TABLE <table_name> ADD -- CONSTRAINT <constraint_name> -- PRIMARY KEY ( -- <column_name_1>, -- <column_name_2>,... -- <column_name_N> ); ALTER TABLE alumno ADD CONSTRAINT alumno_pkc PRIMARY KEY( id_alumno ); ALTER TABLE leccion ADD CONSTRAINT leccion_pkc PRIMARY KEY( id_leccion ); ALTER TABLE examen ADD CONSTRAINT examen_pkc PRIMARY KEY( id_examen ); ALTER TABLE tema ADD CONSTRAINT tema_pkc PRIMARY KEY( id_tema ); ALTER TABLE examen_alumno ADD CONSTRAINT examen_alumno_pkc PRIMARY KEY(
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 6 de 121

id_examen, id_alumno ); ALTER TABLE examen_leccion ADD CONSTRAINT examen_leccion_pkc PRIMARY KEY( id_examen, id_leccion ); --llaves foraneas FKC -- ALTER TABLE <table_name> ADD -- CONSTRAINT <constraint_name> -- FOREIGN KEY ( -- <column_name_1>, -- <column_name_2>,... -- <column_name_N> ) -- REFERENCES <referenced_table_name> ( -- <column_name_1>, -- <column_name_2>,... -- <column_name_N> ); ALTER TABLE tema ADD CONSTRAINT tema_fkc FOREIGN KEY( id_leccion) REFERENCES leccion( id_leccion);

Ejercicio3: EscribaelcdigoDDLqueincluirlosndicesenlastablascreadasanteriormente, previamentesedebedecidirquecamposconvienedefinircomondicesbasndoseenlaidea dequeunndicesecreasobreelcampooloscamposquesernconsultados. Tablaalumno:esprobablequelasconsultasserealicenporelcdigooporelapellido,porlo quepodraserunabuenaideacreardosndicesparaestatabla.Nopareceserunabuena ideadefinirunndicesobrelallaveprimaria,pueslasconsultasnoserealizarnsobreesta columna.Adicionalmentedichondiceyaexiste.

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 7 de 121

SolucinPropuesta:
------indices para las tablas CREATE [UNIQUE] INDEX <index_name> on <table_name> ( <column_name_1>, <column_name_2>, <column_name_N> );

CREATE UNIQUE INDEX alumno_ik1 on alumno( documento_alumno); CREATE INDEX alumno_ik2 on alumno( apellido_alumno); CREATE INDEX leccion_ik2 on leccion( fecha); CREATE INDEX tema_ik1 on tema( nombre_tema);

Inserteahoralossiguientesdatosenlastablas: id_alumno 1 2 id_leccion 1 2 id_examen 1 documento_alumno 79799331 53783975 nombre_alumno Jose Paola fecha 17Feb09 18Feb09 descripcion_examen Primerarevision apellido_alumno Martinez Hernandez

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 8 de 121

id_tema 1 2 id_examen 1 id_examen 1 1 Cdigosugerido(DDL):

nombre_tema presentacion ModelamientoDB

descripcion_tema

id_leccion 1 1

id_leccion 1 id_alumno 1 2 fecha 18Feb2009 18Feb2009 calificacion 4 5

/*INSERT INTO <table_name> ( <column_name_1>, <column_name_2>, ... <column_name_N> ) VALUES ( <column_value_1>, <column_value_2>,... <column_value_N> );*/ INSERT INTO alumno( id_alumno, documento_alumno, nombre_alumno, apellido_alumno ) VALUES(1, '79799331', 'Jose', 'Martinez' ); INSERT INTO alumno( id_alumno, documento_alumno, nombre_alumno, apellido_alumno )
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 9 de 121

VALUES(2, '53783975', 'Paola', 'Hernandez' ); INSERT INTO LECCION( id_leccion, fecha) VALUES(1, '17-Feb-09' ); INSERT INTO LECCION( id_leccion, fecha) VALUES(2, '18-Feb-09' ); INSERT INTO EXAMEN( id_examen, descripcion_examen) VALUES(1, 'Primera Revision' ); INSERT INTO TEMA( id_tema, nombre_tema, descripcion_tema, id_leccion ) VALUES(1, 'presentacion', NULL, 1 ); INSERT INTO TEMA( id_tema, nombre_tema, descripcion_tema, id_leccion
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 10 de 121

) VALUES(2, 'Modelamiento DB', NULL, 1 ); INSERT INTO examen_leccion( id_examen, id_leccion) VALUES(1, 1 ); INSERT INTO examen_alumno( id_examen, id_alumno, fecha, calificacion) VALUES(1, 1, '18-Feb-09', 4 ); INSERT INTO examen_alumno( id_examen, id_alumno, fecha, calificacion) VALUES(1, 2, '18-Feb-09', 5 );

1.1PrimerejemplodeprogramacinconPL/SQL:Triggers LosTriggerssonacciones(programas)queseejecutandeformaautomticacuandouna accinserealizaovaarealizarsesobreunatabla(entidad) Requisito:InsertarinformacinenlatablamediantelainstruccinINSERT

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 11 de 121

Ejercicio4: EscribaunTriggerqueevitequeunalumnorecibalacalificacinde0enunexamenysolicite alprofesorquegenerosamentelaconviertaenun1. SolucinPropuesta:


CREATE OR REPLACE TRIGGER benice_trg BEFORE INSERT ON examen_alumno FOR EACH ROW BEGIN IF :new.calificacion < 1 THEN raise_application_error(-20000, 'Sr. Instructor recuerde que la calificacion minima admitida es 1'); END IF; END;

IngreseahoralossiguienteregistrosempleandoloscdigosDDLcorrespondientes: id_alumno 3 documento_alumno 989056789 nombre_alumno Juan apellido_alumno Perez

id_examen 1

id_alumno 3

fecha 18Feb09

calificacion 0

Culessonloscdigos?
INSERT INTO alumno( id_alumno, documento_alumno, nombre_alumno, apellido_alumno ) VALUES(3, '989056789', 'Juan', 'Perez' ); INSERT INTO examen_alumno(
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 12 de 121

id_examen, id_alumno, fecha, calificacion) VALUES(1, 3, '18-Feb-09', 0 );

Quocurrecuandoseintentainsertarelsegundoregistro? 1.2ConsultassobrelaInformacinAlmacenada: SELECT:seemplealainstruccinSELECTpararecuperarinformacindeunatablaoun conjuntodetablas. Consultasbsicasdeejemplo: Recuperarlascalificacionesobtenidasporunalumnoespecficoalolargodelcurso:


SELECT * FROM EXAMEN_ALUMNO WHERE ID_ALUMNO = 1

SeleccionarlasusandoeldocumentonoelID:
SELECT * FROM EXAMEN_ALUMNO, ALUMNO WHERE EXAMEN_ALUMNO.ID_ALUMNO = ALUMNO.ID_ALUMNO AND ALUMNO.DOCUMENTO_ALUMNO LIKE '53783975'

Otraformadelograrelmismoresultado:
SELECT * FROM EXAMEN_ALUMNO JOIN ALUMNO ON EXAMEN_ALUMNO.ID_ALUMNO = ALUMNO.ID_ALUMNO WHERE ALUMNO.DOCUMENTO_ALUMNO LIKE '53783975'

Peroculeslaventajadeemplearlasegundaforma? ModifiqueelcdigoSQLanteriorparalograrunprimerreporte:
SELECT * FROM EXAMEN_ALUMNO JOIN ALUMNO ON EXAMEN_ALUMNO.ID_ALUMNO = ALUMNO.ID_ALUMNO

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 13 de 121

Deberaobteneralgocomolosiguiente:

YsienlugardelaconsultaanteriorutilizaelsiguientecdigoSQL:
SELECT EA.ID_EXAMEN, EA.FECHA, EA.CALIFICACION, A.DOCUMENTO_ALUMNO, A.NOMBRE_ALUMNO, A.APELLIDO_ALUMNO FROM EXAMEN_ALUMNO EA JOIN ALUMNO A ON EA.ID_ALUMNO = A.ID_ALUMNO;

Verunreportemsadecuado:

Reportesugeridoenclase:
SELECT A.APELLIDO_ALUMNO, A.NOMBRE_ALUMNO, E.DESCRIPCION_EXAMEN, EA.CALIFICACION FROM ALUMNO A JOIN EXAMEN_ALUMNO EA ON A.ID_ALUMNO = EA.ID_ALUMNO JOIN EXAMEN E ON E.ID_EXAMEN = EA.ID_EXAMEN;

YestaconsultasepodraconvertirenunaVista,quedeacuerdoconlaspalabrasdelautor dellibrogua1puededefinirsecomo:

1 El material que se emple inicialmente como gua en este curso corresponde al libro: Beginning PL/SQL: From Novice to Professional escrito por Don Bales. Sitio web: http://www.apress.com/book/view/9781590598825
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 14 de 121

1.3Vistas: AviewrepresentsthedefinitionofaSQLquery(SELECTstatement)asthoughitwerejust anothertableinthedatabase.Hence,youcanINSERTintoandUPDATE,DELETE,and SELECTfromaviewjustasyoucananytable... Laformadedefinirunavistaeslasiguiente: CREATE[ORREPLACE]VIEW<view_name>AS <sql_select_statement>; Deacuerdoconlocuallavistadeseadapuededefinirsedelasiguientemanera:


CREATE OR REPLACE VIEW vista_reporte1 AS SELECT EA.ID_EXAMEN, EA.FECHA, EA.CALIFICACION, A.DOCUMENTO_ALUMNO, A.NOMBRE_ALUMNO, A.APELLIDO_ALUMNO FROM EXAMEN_ALUMNO EA JOIN ALUMNO A ON EA.ID_ALUMNO = A.ID_ALUMNO;

UPDATE:estainstruccinseempleacuandosedeseaactualizarunregistroexistenteenuna tabla.Suformageneraleslasiguiente: UPDATE<table_name> SET<column_name_1>=<column_value_1>, <column_name_2>=<column_value_2>,... <column_name_N>=<column_value_N>; ActualizarlacalificacindelalumnoJuanPerez(id3)enelexamenpresentadoel18Feb09 (id1):


UPDATE EXAMEN_ALUMNO SET CALIFICACION = 3 WHERE ID_ALUMNO = 3 AND ID_EXAMEN = 1;

Luegodeejecutarestaconsultapuedeconsultarselavistacreadaanteriormenteconelfinde verreflejadosloscambios:
SELECT * FROM VISTA_REPORTE1;

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 15 de 121

Ejercicio5: EscribaelcdigoSQLquepermitaactualizalacalificacindeJuanPerezenelexamen presentadoel18Feb09paraelcasoenquenoseconoceelIDdelalumnoperosisu DOCUMENTO:


UPDATE EXAMEN_ALUMNO SET CALIFICACION = 3.5 WHERE ID_ALUMNO = (SELECT ID_ALUMNO FROM ALUMNO WHERE DOCUMENTO_ALUMNO LIKE '989056789') AND ID_EXAMEN = 1;

LaseccindelcdigoqueseencuentraresaltadaseconocecomounSUBQUERY. Cuandosedeseaeliminarunregistroexistenteenunatablaseemplealainstruccin DELETE. Notaalpie:cuandosedeseeeliminarunaconstraintpreviamentedefinidasepuedeemplear lasiguienteinstruccin:


ALTER TABLE <nombre_tabla> DROP CONSTRAINT <nombre_constraint>

Ejercicio6: Insertarunnuevoalumnoconlossiguientesdatos:4,11111,Alumno,Eliminado
INSERT INTO ALUMNO VALUES(4,'11111','Alumno','Eliminado');

Dichoalumnosepuedeeliminarconlainstruccin:
DELETE FROM ALUMNO WHERE ID_ALUMNO = 4;

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 16 de 121

2.IntroduccinaPL/SQL PL/SQLesunlenguajedeprocedimientosqueextiendelasfuncionalidadesdellenguaje SQL. UnprogramaescritoenPL/SQLcombinabloquesdecdigoquegeneralmentecumplen conunatareaespecfica.Existendosclasesdebloques,aquellosquellevannombreylos bloquesannimos.Losbloquesconnombregeneralmentesepuedenalmacenarenlabase dedatosyserinvocadosluegoparacumplirconlatareaparalaquefueronescritos.Los bloquesannimosporsupartesloexistenalmomentodesuejecucinynopuedenser llamadosluegodesdeotrosprogramasobloques. LaestructurafundamentaldeunbloquePL/SQLeslasiguiente: DECLARE Sentenciasdedeclaracin BEGIN Sentenciasdeejecucin EXCEPTION Sentenciasparaelmanejodeexcepciones END; nicamentesonobligatoriaslasSentenciasdeejecucin. Ejemplo1: ConstruyaunbloquePL/SQLqueextraigaelnombreyelapellidodelalumnocuyoid=1
BEGIN SELECT NOMBRE_ALUMNO, APELLIDO_ALUMNO FROM ALUMNO WHERE ID_ALUMNO = 1 END;

Supongaahoraquequeremosmostrarenpantallalainformacinextradayrealizarsobrela mismaalgntipodeoperacin.

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 17 de 121

Ejemplo2:
DECLARE aux_nombre varchar2(50); aux_apellido varchar2(50); BEGIN SELECT NOMBRE_ALUMNO, APELLIDO_ALUMNO INTO aux_nombre, aux_apellido FROM ALUMNO WHERE ID_ALUMNO = 1; DBMS_OUTPUT.PUT_LINE ('Estudiante seleccionado: '||aux_nombre||' '|| aux_apellido); END; /

DBMS_OUTPUT.PUT_LINEisacalltotheprocedurePUT_LINE.Thisprocedureisapartof theDBMS_OUTPUTpackagethatisownedbytheOracleuserSYS. Elcaracter/enlaltimalneaesrequeridopueseselresponsabledequeelcdigosea ejecutado. Ejemplo3:


DECLARE aux_nombre varchar2(50); aux_apellido varchar2(50); BEGIN SELECT NOMBRE_ALUMNO, APELLIDO_ALUMNO INTO aux_nombre, aux_apellido FROM ALUMNO WHERE ID_ALUMNO = 1; DBMS_OUTPUT.PUT_LINE ('Estudiante seleccionado (en altas): '|| upper(aux_nombre)||' '||upper(aux_apellido)); END; /

Supongaahoraqueseingresaunid_alumnoquenoseencuentraenlabasededatos,en estecasonohabrinformacinquemostraryesmuyrecomendablemanejarla excepcinquesepresenta.

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 18 de 121

Ejemplo4:
DECLARE aux_nombre varchar2(50); aux_apellido varchar2(50); BEGIN SELECT NOMBRE_ALUMNO, APELLIDO_ALUMNO INTO aux_nombre, aux_apellido FROM ALUMNO WHERE ID_ALUMNO = 12; DBMS_OUTPUT.PUT_LINE ('Estudiante seleccionado (en altas): '|| upper(aux_nombre)||' '||upper(aux_apellido)); EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE ('No existe un estudiante con el id: 12 '); END; /

2.1Variablesdesustitucin: Unaprimeraformadeobtenerinformacinporpartedelusuarioparaluegoutilizarladentro deunbloqueoprogramaPL/SQLesempleandovariablesdesustitucin. Ejercicio1: EscribaenPL/SQLunbloquequepresentelamundialmentefamosafrase:HolaMundoen maysculas


DECLARE aux_hola varchar2(50) := 'Hola Mundo'; BEGIN DBMS_OUTPUT.PUT_LINE(upper(aux_hola)); END; /

Ejercicio2: ModifiqueelcdigoanteriorparaqueenlugardeHolaMundo,elusuarioveaunalneade salidaconsunombre.Ejemplo:sielusuarioingresaelnombreJose,lasalidadelprograma serHolaJoseenmaysculas

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 19 de 121

DECLARE aux_hola varchar2(50) := 'Hola'; aux_nombre varchar2(50) := '&nombre_usuario'; BEGIN DBMS_OUTPUT.put_line(upper(aux_hola)||' '||upper(aux_nombre)); END; . /

Enelcasoanterior&nombre_usuarioesunavariabledesustitucin,suvalorsersolicitado alusuariocuandoseejecuteelbloquePL/SQLyelvaloringresadoserempleadoenlas SentenciasdeEjecucin. SQL*Plus:parapodertrabajarconelbloqueanterioresnecesariohacerusodelaconsola SQL*Plus,unclienteenmodotextoqueproveeOracle2parainteractuarconlabasededatos. Laprimerainstruccinquedebeejecutarsetanprontocomoselanzaelclientees: connectsystem/password@localhostconloqueseestablecelaconexinalservidorde basesdedatos.Unavezconectadoalservidorescribaelcdigoanterioryobserveel resultadodelaejecucin:


SQL> Enter value for nombre_usuario: jose andres old 3: aux_nombre varchar2(50) := '&nombre_usuario'; new 3: aux_nombre varchar2(50) := 'jose andres'; PL/SQL procedure successfully completed.

Observequeenningnmomentoaparecelacadenadeseada,siesteessucasomodifique elbloquePL/SQLaadiendolasiguientelnea:SETSERVEROUTPUTON;alcomienzodel mismo


SET SERVEROUTPUT ON; DECLARE aux_hola varchar2(50) := 'Hola'; aux_nombre varchar2(50) := '&nombre_usuario'; BEGIN DBMS_OUTPUT.put_line(upper(aux_hola)||' '||upper(aux_nombre)); END; .
2 Los ejemplos y ejercicios expuestos en este documento han sido probados con la versin Express de Oracle 10g, la cual se encuentra disponible sin costo en la siguiente direccin: http://www.oracle.com/technology/software/products/database/xe/index.html
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 20 de 121

Notaalgunadiferencia? Bydefault,SQL*PLUSdoesn'treadwhataPL/SQLprogramhaswrittenwithdbms_output. Withsetserveroutputon,thisbehaviourischanged. Ejercicio1: EscribaunprogramaPL/SQLquesolicitealusuarioeldocumentodeunalumno,consultesu informacinenlabasededatosypresentesunombreyapellidojuntoconlacalificacin obtenidaenelexamencuyoides1.Encasodequeeldocumentoingresadonocoincidacon lainformacindeningnestudiantedebelanzarseunaexcepcinqueinformealusuariode lanoexistenciadedatosparaesedocumento.


SET SERVEROUTPUT ON; DECLARE documento varchar2(20) := '&documento_ingresado'; id_examen number := 1; aux_nombre varchar2(50); aux_apellido varchar2(50); aux_calificacion number; BEGIN SELECT A.NOMBRE_ALUMNO, A.APELLIDO_ALUMNO, EA.CALIFICACION INTO aux_nombre, aux_apellido, aux_calificacion FROM ALUMNO A JOIN EXAMEN_ALUMNO EA ON A.ID_ALUMNO = EA.ID_ALUMNO WHERE A.DOCUMENTO_ALUMNO LIKE documento; DBMS_OUTPUT.PUT_LINE('Informacion Solicitada: '||aux_nombre||' '|| aux_apellido||' '||aux_calificacion); EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('No existe informacion para el documento: '|| documento); END; . /

Paraevitarlasalidaenpantalladelvalororiginalyelvalorsustituidosemodificaelcdigode lasiguientemanera:

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 21 de 121

SET VERIFY OFF; SET SERVEROUTPUT ON ; DECLARE documento varchar2(20) := '&documento_ingresado'; id_examen number := 1; aux_nombre varchar2(50); aux_apellido varchar2(50); aux_calificacion number; BEGIN SELECT A.NOMBRE_ALUMNO, A.APELLIDO_ALUMNO, EA.CALIFICACION INTO aux_nombre, aux_apellido, aux_calificacion FROM ALUMNO A JOIN EXAMEN_ALUMNO EA ON A.ID_ALUMNO = EA.ID_ALUMNO WHERE A.DOCUMENTO_ALUMNO LIKE documento; DBMS_OUTPUT.PUT_LINE('Informacion Solicitada: '||aux_nombre||' '|| aux_apellido||' '||aux_calificacion); EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('No existe informacion para el documento: '|| documento); END; . /

Elsmbolo.IndicaelfinaldeunbloquePL/SQLempleandoSQL*Plus PreguntatipoCertificacin: Quocurrealejecutarelsiguientecdigo:


SET SERVEROUTPUT ON; DECLARE v_var1 NUMBER(2) := 123; v_var2 NUMBER(3) := 123; v_var3 NUMBER(5,3) := 123456.123; BEGIN DBMS_OUTPUT.PUT_LINE('v_var1: '||v_var1); DBMS_OUTPUT.PUT_LINE('v_var2: '||v_var2); DBMS_OUTPUT.PUT_LINE('v_var3: '||v_var3); END;

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 22 de 121

Respuesta:Ladeclaracindev_var1yv_var3especificantamaosmenoresalosvalores querecibenalmomentodesuinicializacin,selanzaunerroralmomentodesucompilacin. Resultainteresantedefinirlostiposdelasvariablescomoobjetosexistentesenlabasede datos,deestamaneraseevitaeltenerquemodificarelcdigoPL/SQLcuandouncambiose realizasobrelabasededatos.Empleandoelmismomodelodebasededatosqueseha venidoutilizandohastaestepuntoconsiderelastablasalumnoyexamen_alumno:

Elcdigodelejercicioplanteadopuedereescribirsedelasiguientemanera:
SET SERVEROUTPUT ON; DECLARE documento ALUMNO.DOCUMENTO_ALUMNO%TYPE := '&documento_ingresado'; id_examen EXAMEN_ALUMNO.ID_EXAMEN%TYPE := 1; aux_nombre ALUMNO.NOMBRE_ALUMNO%TYPE; aux_apellido ALUMNO.APELLIDO_ALUMNO%TYPE; aux_calificacion EXAMEN_ALUMNO.CALIFICACION%TYPE; BEGIN SELECT A.NOMBRE_ALUMNO, A.APELLIDO_ALUMNO, EA.CALIFICACION INTO aux_nombre, aux_apellido, aux_calificacion FROM ALUMNO A JOIN EXAMEN_ALUMNO EA ON A.ID_ALUMNO = EA.ID_ALUMNO WHERE A.DOCUMENTO_ALUMNO LIKE documento; DBMS_OUTPUT.PUT_LINE('Informacion Solicitada: '||aux_nombre||' '|| aux_apellido||' '||aux_calificacion);
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 23 de 121

EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('No existe informacion para el documento: '|| documento); END; . /

Estecambioeneltipodelasvariablesutilizadas,garantizarquesienunfuturosecambia porejemploeltamaooeltipodedatodelcampoparaNOMBRE_ALUMNOelprogramaPL/ SQLsigafuncionandosinnecesidaddemodificarloenabsoluto.

2.2TiposdedatossoportadosenPL/SQL: Declaracin VARCHAR2(tamao) CHAR[(tamao)] CapacidadMxima 4000bytes 2000bytes Sinosedefineuntamaoel valorpordefectoesde1byte (1caracter) Observacin

NUMBER[(#dgitos, precisin)] DATE TIMESTAMP BOOLEAN LONG Almacenalafecha+lahora conunaprecisinde9dgitos TRUE/FALSE/NULL ExtiendeelVARCHAR2 permitiendoalmacenarhasta 2GB LargeObject4GBde almacenamiento.Seemplea paraelalmacenamientode objetosbinarioscomoVideos, Imgenes,Audios,etc...

LOB

Whenyoureaprogrammer,itisimportanttoknowtheoperatorsthatyoucanuseina programminglanguage.Theydetermineyourvariousoptionsforsolvingaprogrammatic problem.ThefollowingaretheoperatorsyoucanuseinPL/SQL:


Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 24 de 121

Arithmetic(**,*,/,+,) Comparison(=,<>,!=,<,>,<=,>=,LIKE,IN,BETWEEN,ISNULL,ISNOTNULL, NOTIN) Logical(AND,OR,NOT) String(||,LIKE) Expressions Operatorprecedence **,NOT +,(arithmeticidentityandnegation)*,/,+,,||=,<>,!=,<= >=,<,>,LIKE,BETWEEN,IN,ISNULL AND(logicalconjunction) OR(logicalinclusion)

2.3Bloquesanidadosycontroldeflujo: Pararealizarlossiguientesejerciciosesnecesarioingresarlossiguientesdatosenlastablas correspondientes: LECCION: id_leccion 3 EXAMEN id_examen 2 descripcion_examen 2doexamen Fecha 19Feb09

EXAMEN_LECCION: id_examen 2 2 id_leccion 2 3

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 25 de 121

EXAMEN_ALUMNO: id_examen 2 2 2 Ejercicio: DefinaelcdigoDDLnecesarioparalainsercindelosanterioresdatosenlastablas correspondientesyejecteloempleandolaconsoladeSQL*Plus. Notaalpie:alestartrabajandoenlaconsolaSQL*Pluselautocommitseencuentrapor defectodesactivado,paraalmacenarlosdatosdeseadosdebemodificarseestacondicin mediantelainstruccinSET AUTOCOMMIT ON; Thechangestothedatabasethathavebeenexecutedbyasingleapplicationsessionarenot actuallysavedtothedatabaseuntilaCOMMIToccurs.Workwithinatransactionuptoand justbeforethecommitcanberolledback;afteracommithasbeenissued,workwithinthat transactioncannotberolledback. NotethatthoseSQLstatementsshouldbeeithercommittedorrejectedasagroup. EsposibleemplearelSELECTconalgunosoperadoresadicionalesqueproveenresultados interesantes: SELECTSUM(campo)permiteobtenerelresultadodesumarloscontenidosdeun determinadocampo.Asporejemplosisedeseaobtenerlasumadetodaslascalificaciones almacenadasenlatablaEXAMEN_ALUMNOpuedeejecutarseelsiguientecdigo:
SELECT SUM(calificacion) FROM EXAMEN_ALUMNO;

id_alumno 1 2 3

fecha 23Feb09 23Feb09 23Feb09

calificacion 2 1.5 2.5

SELECTCOUNT(campo)permitedeterminarelnmerototalderegistrosquecumplancon uncriterio,porejemploparadeterminarelnmerodecalificacionesdisponiblesparaun alumnoenparticularpuedeejecutarseelsiguientecdigoSQL:


SELECT COUNT(ID_EXAMEN) FROM EXAMEN_ALUMNO WHERE ID_ALUMNO = 2;

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 26 de 121

Ejercicio: EscribaunprogramaenPL/SQLquepermitabasadoenlascalificacionesobtenidasenlos exmenes1y2determinarsiunalumnoseencuentraaprobandoelcurso(calificacin promediomayoroiguala3)osiporelcontrariocorreriesgodereprobarlo(calificacin promedioinferiora3).EmpleeIFCONDICIONTHENparacontrolarelflujodelaaplicacin basadoenlasconsultasrealizadas. SolucinPropuesta:


--23 de Febrero - 09 SET SERVEROUTPUT ON; DECLARE aux_documento ALUMNO.DOCUMENTO_ALUMNO%TYPE := '&documento_ingresado'; aux_nombre ALUMNO.NOMBRE_ALUMNO%TYPE; aux_apellido ALUMNO.APELLIDO_ALUMNO%TYPE; aux_sumatoria EXAMEN_ALUMNO.CALIFICACION%TYPE; aux_numero_examenes NUMBER; BEGIN --se recuperan y se suman las calificaciones disponibles para el documento definido SELECT SUM(EA.CALIFICACION) INTO aux_sumatoria FROM EXAMEN_ALUMNO EA JOIN ALUMNO A ON EA.ID_ALUMNO = A.ID_ALUMNO WHERE A.DOCUMENTO_ALUMNO LIKE aux_documento; --se determina el numero de examenes presentados por ese alumno en particular SELECT COUNT(EA.ID_EXAMEN) INTO aux_numero_examenes FROM EXAMEN_ALUMNO EA JOIN ALUMNO A ON EA.ID_ALUMNO = A.ID_ALUMNO WHERE A.DOCUMENTO_ALUMNO LIKE aux_documento; /*se divide la sumatoria de las calificaciones entre el numero de examenes y se evalua frente a la nota minima para aprobar: 3.0*/ IF((aux_sumatoria/aux_numero_examenes) >= 3.0) THEN DBMS_OUTPUT.PUT_LINE('EL ALUMNO VA APROBANDO EL CURSO HASTA EL MOMENTO');
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 27 de 121

ELSIF (aux_numero_examenes 3.0) THEN DBMS_OUTPUT.PUT_LINE('EL MOMENTO'); ELSE --ocurre cuando no se DBMS_OUTPUT.PUT_LINE('NO aux_documento); END IF;

> 0 AND (aux_sumatoria/aux_numero_examenes) >= ALUMNO VA REPROBANDO EL CURSO HASTA EL encuentran examenes presentados por ese alumno EXISTEN REGISTROS PARA EL DOCUMENTO: '||

EXCEPTION --observe como nunca se llega a esta excepcion WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('NO EXISTEN REGISTROS PARA EL DOCUMENTO: '|| aux_documento); END; .

/ 2.4Funciones: Elejercicioanteriorpuededefinirsecomounafuncinyalmacenarseenlabasededatos, paraserejecutadaluego: CREATE[ORREPLACE]FUNCTION<function_name>[( <parameter_name_1>[IN][OUT]<parameter_data_type_1>, <parameter_name_2>[IN][OUT]<parameter_data_type_2>,... <parameter_name_N>[IN][OUT]<parameter_data_type_N>)] RETURN<return_data_type>IS thedeclarationsection BEGIN theexecutablesection return<return_data_type>; EXCEPTION theexceptionhandlingsection END; / Aplicandoestasintaxisalejercicioanterior:
--23 de Febrero - 09 - version funcion CREATE OR REPLACE FUNCTION get_basic_info(documento IN VARCHAR2) RETURN VARCHAR2

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 28 de 121

IS --asignando el parametro de entrada aux_documento ALUMNO.DOCUMENTO_ALUMNO%TYPE := documento; aux_nombre ALUMNO.NOMBRE_ALUMNO%TYPE; aux_apellido ALUMNO.APELLIDO_ALUMNO%TYPE; aux_sumatoria EXAMEN_ALUMNO.CALIFICACION%TYPE; aux_numero_examenes NUMBER; BEGIN --se recuperan y se suman las calificaciones disponibles para el --documento definido SELECT SUM(EA.CALIFICACION) INTO aux_sumatoria FROM EXAMEN_ALUMNO EA JOIN ALUMNO A ON EA.ID_ALUMNO = A.ID_ALUMNO WHERE A.DOCUMENTO_ALUMNO LIKE aux_documento; --se determina el numero de examenes presentados por ese alumno en --particular SELECT COUNT(EA.ID_EXAMEN) INTO aux_numero_examenes FROM EXAMEN_ALUMNO EA JOIN ALUMNO A ON EA.ID_ALUMNO = A.ID_ALUMNO WHERE A.DOCUMENTO_ALUMNO LIKE aux_documento; /*se divide la sumatoria de las calificaciones entre el numero de examenes y se evalua frente a la nota minima para aprobar: 3.0*/ IF((aux_sumatoria/aux_numero_examenes) >= 3.0) THEN RETURN ('EL ALUMNO VA APROBANDO EL CURSO HASTA EL MOMENTO'); ELSIF (aux_numero_examenes > 0 AND (aux_sumatoria/aux_numero_examenes) >= 3.0) THEN RETURN ('EL ALUMNO VA REPROBANDO EL CURSO HASTA EL MOMENTO'); ELSE --ocurre cuando no se encuentran examenes presentados por ese alumno RETURN ('NO EXISTEN REGISTROS PARA EL DOCUMENTO: '||aux_documento); END IF; EXCEPTION --observe como nunca se llega a esta excepcion WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('NO EXISTEN REGISTROS PARA EL DOCUMENTO: '|| aux_documento); END; /
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 29 de 121

Unlikemostcompilers,whichwilldisplayalistingoferrorsfoundinsourcecode,Oracle storesanyerrorsitfindsinadatabasetablenamedUSER_ERRORS.Ifyouwanttoseethe specificdetails,andyoumaywell,youneedtoretrievetheerrorlistingyourself.Usethe SQL*PluscommandSHOWERRORS Paraverificarsilafuncinanteriorfuncionacorrectamentepuedeejecutarlasiguientelnea enlaconsolaSQL*Plus:SELECT get_basic_info('79799330') FROM DUAL; HaveyounoticedthatImusingatablebythenameofdualintheconditionalINSERT... SELECTstatement?dualisatableownedbytheOracledatabase(ownerSYS)thathasone columnandonerow.Itisveryhandy,becauseanytimeyouselectagainstthistable,youget one,andonlyone,rowback. ... SeehowusingdualtotesthowaSQLfunctionmightworkcanbehandy?Itallowsyouto hackawaywithoutanyhugecommitmentincode. Ejercicio: EscribaunafuncinenPL/SQLquerecibacomoparmetrounacadenadetextoydevuelva alasalidaelsiguientemensaje:Ustedingresoeltexto:cadena_ingresada,endonde cadena_ingresadacorrespondealaentradadelusuarioalmomentodellamarlafuncin. SolucinPropuesta:
CREATE OR REPLACE FUNCTION say_hello(cadena IN VARCHAR2) RETURN VARCHAR2 IS mensaje VARCHAR2(50) := 'Usted ingreso el texto: '; BEGIN mensaje := mensaje || cadena; RETURN mensaje; END;

UnidaddePrueba(TestUnit):
select say_hello('hola amigos') from dual;

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 30 de 121

2.5AdicindeSecuenciasalasTablas: HastaelmomentocadavezquesedeseainsertarunregistroenlatablaALUMNOes necesarioespecificarelvalorparalacolumnaID_ALUMNO,peroenrealidadesevalor deberaserdeterminadoporlabasededatosdeformaautomtica.Paramodificarel comportamientodelatablaeinsertarlosregistrossintenerqueocuparsedelltimoid empleadosecrearunasecuencia: AnOraclesequenceisanOracledatabaseobjectthatcanbeusedtogenerateunique numbers.Youcanusesequencestoautomaticallygenerateprimarykeyvalues. CREATESEQUENCEsequence_name MINVALUEvalue MAXVALUEvalue STARTWITHvalue INCREMENTBYvalue CACHEvalue; MINVALUE:valormnimodelasecuencia,paraelcasodelatablaqueseestpresentando sernecesarioespecificarelsiguientevaloralltimoiddefinidomanualmente,aspor ejemplo,sisutablacuentaconlasiguienteinformacin:

ElvalordelparmetroMINVALUEdebeser4. MAXVALUE:definesiassedeseaelvalormximoquepodrtomarlasecuencia.En casodenoespecificarningnvalorparaesteparmetrosuvalorpordefectoesde 999999999999999999999999999 STARTWITH:eselprimervalorqueseasignaalasecuencia,sisuvalornoseespecificael mismocoincideconelMINVALUE

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 31 de 121

INCREMENTBY:especificaculeselincrementoqueocurreentreunvaloryelsiguiente dentrodelasecuencia. CACHE:esteparmetroopcionalsirveparaalmacenarenmemoriaunnmerodadode valoressiguientesparaaumentarlavelocidadderespuesta.Porejemplosiseespecifica algocomoCACHE20,elprimervalordelasecuenciaserconsultadoalabasededatos, perolos20siguientesestarndisponiblesenunamemoriaintermediadeaccesomsrpido. Sinembargoladesventajadeusaresteparmetroesquesiporalgnmotivoseproduceuna falloenelsistema,losvalorespreviamentealmacenadosenelCACHEseperdernyla secuenciaquedarconunespaciovacoenlamitad(GAP). ExpuestoloanteriorpuededefinirselasentenciaparalatablaALUMNOdelasiguiente manera:
CREATE SEQUENCE seq_alumno MINVALUE 4 START WITH 4 INCREMENT BY 1 NOCACHE

Yahora,parainsertarunpardenuevosregistrosenlatabla,seempleanlossiguientes comandosSQL:
INSERT INTO ALUMNO VALUES (seq_alumno.NEXTVAL,'19900897','LUIS','SILVA'); INSERT INTO ALUMNO VALUES (seq_alumno.NEXTVAL,'45904827','CAMILA','CARDENAS');

Ejercicio: Creelassecuenciasquepermitaninsertarnuevosregistrosenlatablaleccin,exameny tema.


CREATE SEQUENCE seq_leccion MINVALUE 4 START WITH 4 INCREMENT BY 1 NOCACHE CREATE SEQUENCE seq_examen MINVALUE 2
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 32 de 121

START WITH 2 INCREMENT BY 1 NOCACHE CREATE SEQUENCE seq_tema MINVALUE 3 START WITH 3 INCREMENT BY 1 NOCACHE

Ejercicio: DefinaunanuevafuncinenPL/SQLllamadacreate_alumnoquerecibacomoparmetrosel documento,nombreyapellidodeunalumno,creeelregistrocorrespondienteenlatabla ALUMNOyretorneunvalordeTRUEsilaoperacinsepudorealizaroFALSEencaso contrario. SolucinPropuesta:


CREATE OR REPLACE FUNCTION create_alumno(documento IN VARCHAR2, nombre IN VARCHAR2, apellido IN VARCHAR2) RETURN BOOLEAN IS BEGIN INSERT INTO ALUMNO VALUES (seq_alumno.NEXTVAL,documento,nombre,apellido); RETURN TRUE; EXCEPTION WHEN OTHERS THEN RETURN FALSE;--se produjo un error END;

Ejercicio: EscribaelcdigoPL/SQLquelepermitaprobarelfuncionamientodelafuncin create_alumno SolucinPropuesta:


DECLARE estado BOOLEAN:=create_alumno('123144609','Carlos','Peralta');
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 33 de 121

BEGIN IF(estado) THEN DBMS_OUTPUT.PUT_LINE('Alumno creado'); ELSE DBMS_OUTPUT.PUT_LINE('Ocurrio un error en la creacion del alumno'); END IF; END; . /

2.6Commit,RollbackySavepoint: AunquesehamencionadoenunprrafoanteriorelusodeSETAUTOCOMMITONcomo unaalternativaparaobviarelcomportamientotransaccionaldeOracle,enrealidadeste comportamientodebeintegrarsealosprogramaselaboradosenPL/SQL Lasintaxisbsicadeestostrescomandoseslasiguiente: COMMIT[WORK]:hacepermanentesloscambiosdefinidosenunatransaccin,elparmetro WORKesopcional ROLLBACK[WORK]:deshaceloscambiosquesehabandefinidoenlatransaccinanterior, elparmetroWORKesopcional SAVEPOINTNAME:defineunpuntoapartirdelcualsepuedendeshacerloscambiosconel comandoROLLBACK,cualquiercambioquesehayadefinidoantesdelSAVEPOINTnoseve afectadoporelROLLBACK. Ejemplo1: Dadalatablaleccinconelsiguientecontenido: ID_LECCIONFECHA 117FEB09 218FEB09 319FEB09 Inserteunregistroparalafecha23FEB09recuerdequesustablascuentanahoraconuna secuenciaparalageneracinautomticadelallaveprimaria:
INSERT INTO leccion
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 34 de 121

VALUES (seq_leccion.NEXTVAL, '23-FEB-09')

2 ;

DefinaunpuntodeguardadoSAVEPOINT A; Inserteunnuevoregistroparalafecha24Feb09yluegodeshagalosltimoscambios empleando:ROLLBACK TO A; Quinformacintienelatablaleccion?:


select * from leccion; ID_LECCION FECHA ---------- -----------------1 17-FEB-09 2 18-FEB-09 3 19-FEB-09 4 23-FEB-09

CierresusesinconSQL*Plusyvuelvaainiciarlaqudatoscontieneahoralatabla leccion?.

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 35 de 121

2.7RevisinParcialdelovistohastaestepunto: (ElsiguienteejerciciohasidotomadodellibroPL/SQLByExampledeBenjamin RosenzweigyElenaSilvestrovaRakhimov) 1. CreateatablecalledCHAP4withtwocolumns;oneisID(anumber)andtheotheris NAME,whichisaVARCHAR2(20). 2. CreateasequencecalledCHAP4_SEQthatincrementsbyunitsof5. 3. WriteaPL/SQLblockthatdoesthefollowing,inthisorder: 1. Declarestwovariables:oneforv_nameandoneforv_id.Thev_namevariablecan beusedthroughouttheblocktoholdthenamethatwillbeinserted;realizethatthe valuewillchangeinthecourseoftheblock. 2. Theblockinsertsintothetablethenameofthestudentwhoisenrolledinthemost classes3andusesasequencefortheID.AfterwardthereisSAVEPOINTA. 3. Thestudentwiththefewestclasses4isinserted.AfterwardthereisSAVEPOINTB. 4. Theinstructorwhoisteachingthemostcourses5isinsertedinthesameway. AfterwardthereisSAVEPOINTC. 5. UsingaSELECTINTOstatement,holdthevalueoftheinstructorinthevariable v_id. 6. Undotheinstructorinsertionbyusingrollback. 7. Inserttheinstructorteachingthefewestcourses6,butdonotusethesequenceto generatetheID.Instead,usethevaluefromthefirstinstructor,whomyouhave sinceundone. 8. Inserttheinstructorteachingthemostcourses,andusethesequencetopopulate hisorherID. 4. AddDBMS_OUTPUTthroughouttheblocktodisplaythevaluesofthevariablesas theychange.(Thisisgoodpracticefordebugging.)

3 4 5 6

John Doe Marcus Indigus Roy Barnes Louis Mint


Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 36 de 121

SolucinPropuestaPuntos1y2:
-- Create a table called CHAP4 with two columns; -- one is ID (a number) and the other is NAME, -- which is a VARCHAR2(20). DROP TABLE CHAP4; CREATE TABLE CHAP4( ID NUMBER, NAME VARCHAR(20)); -- constraints for this table ALTER TABLE CHAP4 ADD CONSTRAINT CHAP4_PK PRIMARY KEY(ID); -- Create a sequence called CHAP4_SEQ that increments by units of 5. DROP SEQUENCE CHAP4_SEQ; CREATE SEQUENCE CHAP4_SEQ MINVALUE 1 START WITH 1 INCREMENT BY 5;

UstedpuedeguardarestearchivodetextoeinvocarlodesdelaconsoladeSQL*Plus empleandounasintaxissimilaralasiguiente(porfavortengaencuentalaestructurade archivosdesusistemaoperativo):


@/home/jamslug/revisionParcial01.sql

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 37 de 121

SolucinPropuestaPunto3:
SET SERVEROUTPUT ON; DECLARE v_name CHAP4.NAME%TYPE; v_id CHAP4.ID%TYPE; BEGIN v_name := 'John Doe';--the student who is enrolled in the most classes DBMS_OUTPUT.PUT_LINE(v_name); INSERT INTO CHAP4 VALUES(CHAP4_SEQ.NEXTVAL,v_name); SAVEPOINT A; v_name := 'Marcus Indigus';--the student with the fewest classes DBMS_OUTPUT.PUT_LINE(v_name); INSERT INTO CHAP4 VALUES(CHAP4_SEQ.NEXTVAL,v_name); SAVEPOINT B; v_name := 'Roy Barnes';--the instructor who is teaching the most courses DBMS_OUTPUT.PUT_LINE(v_name); INSERT INTO CHAP4 VALUES(CHAP4_SEQ.NEXTVAL,v_name); SAVEPOINT C; SELECT ID INTO v_id FROM CHAP4 WHERE NAME LIKE 'Roy Barnes'; ROLLBACK TO B; v_name := 'Louis Mint';--the instructor teaching the fewest courses

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 38 de 121

DBMS_OUTPUT.PUT_LINE(v_id||' '||v_name); -- do not use the sequence to generate the ID. Instead, use the value -- from the first instructor, whom you have since undone. INSERT INTO CHAP4 VALUES(v_id,v_name); COMMIT; v_name := 'Roy Barnes';--the instructor who is teaching the most courses DBMS_OUTPUT.PUT_LINE(v_name); INSERT INTO CHAP4 VALUES(CHAP4_SEQ.NEXTVAL,v_name); COMMIT; END; . /

UstedpuedeguardarestearchivodetextoeinvocarlodesdelaconsoladeSQL*Plus empleandounasintaxissimilaralasiguiente(porfavortengaencuentalaestructurade archivosdesusistemaoperativo):


@/home/jamslug/revisionParcial02.sql

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 39 de 121

3.ProcedimientosAlmacenados: Demanerasimilaracomosedefinenfuncionesparasualmacenamientoyposteriorre utilizacinenPL/SQLpuedendefinirseprocedimientos.Ladiferenciafundamentalentreuna funcinyunprocedimientoesqueesteltimonoRETORNAningnvalor.Lasintaxisgeneral paradefinirunprocedimientoeslasiguiente:


CREATE [OR REPLACE] PROCEDURE <procedure_name> [( <parameter_name_1> [IN] [OUT] <parameter_data_type_1>, <parameter_name_2> [IN] [OUT] <parameter_data_type_2>,... <parameter_name_N> [IN] [OUT] <parameter_data_type_N> )] IS --the declaration section BEGIN -- the executable section EXCEPTION -- the exception-handling section END; /

Ejemplo1:NowyoullcreateaprocedurethatwrapstheSYS.DBMS_OUTPUT.put_line() procedure,butusesaveryshortname.Youllendupusingthe SYS.DBMS_OUTPUT.put_line()procedurealot.Itgetstiresometotypea24character methodnameeverytimeyouwanttodisplayalineoftextonthescreeninSQL*Plus.So,to savekeystrokes,youwillgiveyourSYS.DBMS_OUTPUT.put_line()wrapperprocedurethe namepl(),asinpforputandlforline.tomadodellibrogua


CREATE OR REPLACE PROCEDURE pl(cadena IN VARCHAR2) IS BEGIN DBMS_OUTPUT.PUT_LINE(cadena); END; . / --TESTING UNIT DECLARE v_aux VARCHAR2(500) := 'Hola alias para la funcion de impresion de linea'; BEGIN pl(v_aux); pl('Este es el primer procedimiento almacenado'); pl(123); END
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 40 de 121

3.1Bloquesdecdigoanidados: UstedpuedeanidarbloquesdePL/SQLdentrodeotrosbloquesdePL/SQL,graciasalocual puedeconstruirprogramasmscomplejosmanejandocondicionesparticularesencadauno delosbloques.Considereelejemploenelqueseconsulta,empleandoeldocumentodeun alumno,sisteseencuentraaprobandooreprobandouncurso.


--24 de Febrero - 09 - version funcion con bloques anidados CREATE OR REPLACE FUNCTION get_basic_info(documento IN VARCHAR2) RETURN VARCHAR2 IS aux_documento ALUMNO.DOCUMENTO_ALUMNO%TYPE := documento;--asignando el parametro de entrada aux_nombre ALUMNO.NOMBRE_ALUMNO%TYPE; aux_apellido ALUMNO.APELLIDO_ALUMNO%TYPE; aux_sumatoria EXAMEN_ALUMNO.CALIFICACION%TYPE; aux_numero_examenes NUMBER; BEGIN --se recuperan y se suman las calificaciones disponibles para el documento definido --primer bloque BEGIN SELECT SUM(EA.CALIFICACION) INTO aux_sumatoria FROM EXAMEN_ALUMNO EA JOIN ALUMNO A ON EA.ID_ALUMNO = A.ID_ALUMNO WHERE A.DOCUMENTO_ALUMNO LIKE aux_documento; --manejo de una condicion no valida que no es una EXCEPTION de ORACLE IF(aux_sumatoria=0) THEN RETURN('NO EXISTEN CALIFICACIONES PARA EL DOCUMENTO'); END IF; END;

--se determina el numero de examenes presentados por ese alumno en particular --segundo bloque BEGIN SELECT COUNT(EA.ID_EXAMEN) INTO aux_numero_examenes FROM EXAMEN_ALUMNO EA JOIN ALUMNO A
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 41 de 121

ON EA.ID_ALUMNO = A.ID_ALUMNO WHERE A.DOCUMENTO_ALUMNO LIKE aux_documento; --manejo de una condicion no valida que no es una EXCEPTION de ORACLE IF(aux_numero_examenes=0) THEN RETURN('NO EXISTEN EXAMENES PARA EL DOCUMENTO'); END IF; END; --tercer bloque BEGIN /*se divide la sumatoria de las calificaciones entre el numero de examenes y se evalua frente a la nota minima para aprobar: 3.0*/ IF((aux_sumatoria/aux_numero_examenes) >= 3.0) THEN RETURN('EL ALUMNO VA APROBANDO EL CURSO HASTA EL MOMENTO'); ELSE RETURN('EL ALUMNO VA REPROBANDO EL CURSO HASTA EL MOMENTO'); END IF; END; END; . / --TEST UNIT SELECT get_basic_info('79799330') FROM DUAL;

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 42 de 121

4.Paquetes: Unpaqueteesungrupodefuncionesyprocedimientosqueseempleanpararealizartareas comunes.Puedepensarenelloscomoelequivalentealaslibrerasenotroslenguajesde programacin.Unpaqueteestcompuestopordospartes,suespecificacinysucuerpo. EspecificacindelPaquete:correspondealainterfazpblicadelpaquete,laqueofrecelas funcionesdisponiblesparaserempleadasdesdeotrosprogramasPL/SQL. CuerpodelPaquete:correspondealalgicadetrsdecadaunadelasfuncionesydems recursosqueladescripcinanunciaalpblico. EjemplodePaquete:


CREATE OR REPLACE PACKAGE DATES AS /* dates.pks by Donald J. Bales on 12/15/2006 Additional DATE data type methods. */ -- The maximum and minimum date values. d_MAX constant7 date := to_date8('99991231235959', 'YYYYMMDDHH24MISS'); d_MIN constant date := to_date('-47120101', 'S9YYYYMMDD');
7 CONSTANT se emplea para definir una constante, las cuales al contrario de las variables no sufren modificaciones en su valor asignado durante la ejecucin de un programa PL/SQL 8 to_date(cadena, formato): esta funcin devuelve una fecha correspondiente a la cadena indicada y con el formato especificado. (ms informacin en: http://www.techonthenet.com/oracle/functions/to_date.php). Si se desea obtener una fecha y una hora puede emplearse la funcin to_timestamp(cadena, formato) 9 Observe la diferencia entre usar la S y no usarla en el formato especificado: SQL> select to_date('-47120101','SYYYYMMDD') from dual; TO_DATE('-47120101 -----------------01-JAN-12 SQL> select to_date('-47120101','YYYYMMDD') from dual; select to_date('-47120101','YYYYMMDD') from dual * ERROR at line 1: ORA-01841: (full) year must be between -4713 and +9999, and not be 0 El parmetro S utiliza el siguiente ao al ao especificado, en este caso -4713 en lugar de -4712
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 43 de 121

-- Returns the specified date with the time set to 23:59:59, therefore, -- the end of the day. FUNCTION end_of_day( aid_date in date ) return date; -- Returns constant d_MAX. This is useful in SQL statements where the -- constant DATES.d_MAX is not accessible. FUNCTION get_max return date; -- Returns constant d_MIN. This is useful in SQL statements where the -- constant DATES.d_MIN is not accessible. FUNCTION get_min return date; -- Text-based help for this package. "set serveroutput on" in SQL*Plus. PROCEDURE help; -- Returns a randomly generated date that exists between the years specified. FUNCTION random( ain_starting_year in number, ain_ending_year in number ) return date; -- Returns the specified date with the time set to 00:00:00, therefore, the -- start of the day. FUNCTION start_of_day( aid_date in date ) return date; -- Test unit for this package. PROCEDURE test; end DATES; /

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 44 de 121

@/home/jamslug/date-spec.sql

Elcuerpodeestepaqueteesellugarenelcualsedefinencadaunodelosrecursos expuestosmediantesudescripcin:
CREATE OR REPLACE PACKAGE BODY DATES AS /* dates.pkb by Donald J. Bales on 12/15/2006 Additional DATE data type methods */ FUNCTION end_of_day( aid_date in date ) return date is begin return to_date(to_char(aid_date, 'SYYYYMMDD')10||'235959', 'SYYYYMMDDHH24MISS'); end end_of_day;

FUNCTION get_max return date is begin return d_MAX; end get_max;

FUNCTION get_min return date is begin return d_MIN; end get_min;

FUNCTION random( ain_starting_year in number, ain_ending_year in number )


10 to_char(entrada, formato) convierte a una cadena de caracteres la entrada aplicando el formato especificado. En este caso puntual la entrada es una fecha (DATE). Esta funcin tambin puede emplearse para convertir tipos de dato NUMBER en cadenas de caracteres
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 45 de 121

return date is d_random date; n_day number; n_month number; n_year number; begin n_year := round11(DBMS_RANDOM.value12( ain_starting_year, ain_ending_year), 0); loop13 n_month := round(DBMS_RANDOM.value(1, 12), 0); n_day := round(DBMS_RANDOM.value(1, 31), 0); begin d_random := to_date(lpad14(to_char(n_year), 4, '0')|| lpad(to_char(n_month), 2, '0')|| lpad(to_char(n_day), 2, '0'), 'YYYYMMDD'); EXIT15; exception when OTHERS then

11 round(numero, lugares decimales): redondea el nmero de entrada al nmero de lugares decimales indicado. En este caso devolver un valor entero sin decimales. 12 DBMS_RANDOM es un paquete. La funcin value(n1, n2) definida dentro de ese paquete devuelve un valor aleatorio comprendido entre los lmites especificado. Encuentra ms informacin sobre este paquete en: http://www.psoug.org/reference/dbms_random.html 13 Se emplea la instruccin loop cuando se desea definir un ciclo pero se desconoce el nmero de veces que ste se debe ejecutar y se quiere asegurar que al menos se ejecute una vez (ms informacin en: http://www.techonthenet.com/oracle/ loops/gen_loop.php). El ciclo termina cuando se encuentra la instruccin EXIT o cuando se evala como cierta una condicin EXIT WHEN 14 lpad(cadena, posiciones a rellenar, caracter de relleno): devuelve la cadena con las posiciones a rellenar al comienzo de la misma a su izquierda ocupadas por el caracter de relleno. El siguiente ejemplo ilustra esta idea: select lpad(to_char(2009),4,'0') from dual; LPAD(TO_CHAR -----------2009 select lpad(to_char(09),4,'0') from dual; LPAD(TO_CHAR -----------0009 15 Si ninguna excepcin ha ocurrido el Loop termina en este punto
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 46 de 121

if SQLCODE16 <> -183917 then pl(SQLERRM18); end if; end; end loop; return d_random; end random;

FUNCTION start_of_day( aid_date in date ) return date is begin return trunc19(aid_date); end start_of_day;

-- Write up the help text here in this help method PROCEDURE help is begin pl20('============================== PACKAGE =============================='); pl(chr21(9));
16 SQLCODE es una funcin que devuelve el valor numrico asociado a la ltima excepcin que se haya generado. Puede emplear el siguiente enlace para obtener mas informacin sobre los diferentes valores de error que pueden ocurrir: http:// publib.boulder.ibm.com/infocenter/iseries/v5r3/index.jsp?topic=/rzala/rzalaco.htm 17 Se refiere al valor nmerico de una excepcin que se produce cuando una fecha no se encuentra en el rango vlido (January 1, 4712 BC to December 31, 9999 AD) 18 SQLERRM es una funcin que devuelve el mensaje de error asociado a la ltima excepcin que se haya generado 19 trunc(date, format): devuelve la fecha truncada de acuerdo con el formato especificado: select trunc(to_date('19770217050000','YYYYMMDDHHMISS')) FROM DUAL; TRUNC(TO_DATE('197 -----------------17-FEB-77 select trunc(to_date('19770217','YYYYMMDD'),'Q') from dual; TRUNC(TO_DATE('197 -----------------01-JAN-77 20 pl es un procedimiento almacenado que sirve como envoltorio (wrapper) a la funcin PUT_LINE 21 chr(numero) devuelve el carcter asociado al valor de nmero. Para el caso de chr(9) devuelve un tabulador horizontal (TAB). Si lo desea puede consultar la lista de cdigos ASCII en la siguiente pgina: http://www.techonthenet.com/ascii/chart.php
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 47 de 121

pl('DATES'); pl(chr(9)); pl('------------------------------ CONSTANTS ----------------------------'); pl(chr(9)); pl('d_MAX'); pl(chr(9)||'Represents the maximum value for the DATE data type.'); pl('d_MIN'); pl(chr(9)||'Represents the minimum value for the DATE data type.'); pl(chr(9)); pl('------------------------------ FUNCTIONS ----------------------------'); pl(chr(9)); pl('DATES.end_of_day('); pl('aid_date in date)'); pl('return date;'); pl(chr(9)||'Returns the passed date with the time portion set to the end '); pl(chr(9)||'of the day:'); pl(chr(9)||'23:59:59 (HH24:MI:SS).'); pl(chr(9)); pl('DATES.get_max( )'); pl('return date;'); pl(chr(9)||'Returns the constant DATES.d_MAX.'); pl(chr(9)); pl('DATES.get_min( )'); pl('return date;'); pl(chr(9)||'Returns the constant DATES.d_MIN.'); pl(chr(9)); pl('DATES.random('); pl('ain_starting_year in number,'); pl('ain_ending_year in number)'); pl('return date;'); pl(chr(9)||'Returns a random date that exists between the specified years.'); pl(chr(9)); pl('DATES.start_of_day('); pl('aid_date in date)'); pl('return date;'); pl(chr(9)||'Returns the passed date with the time portion set to the start'); pl(chr(9)||'of the day:'); pl(chr(9)||'00:00:00 (HH24:MI:SS).'); pl(chr(9));
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 48 de 121

pl('------------------------------ PROCEDURES ----------------------------'); pl(chr(9)); pl('DATES.help( );'); pl(chr(9)||'Displays this help text if set serveroutput is on.'); pl(chr(9)); pl('DATES.test( );'); pl(chr(9)||'Built-in test unit. It will report success or error for each'); pl(chr(9)||'test if set'); pl(chr(9)||'serveroutput is on.'); pl(chr(9)); end help;

PROCEDURE test is d_date date; begin pl('============================== PACKAGE ==============================='); pl(chr(9)); pl('DATES'); pl(chr(9)); pl('1. Testing constants d_MIN and d_MAX'); if d_MIN < d_MAX then pl('SUCCESS'); else pl('ERROR: d_MIN is not less than d_MAX'); end if; pl('2. Testing end_of_day()'); if to_char(end_of_day(SYSDATE), 'HH24MISS') = '235959' then pl('SUCCESS'); else pl('ERROR: end_of_day is not 23:59:59'); end if; pl('3. Testing get_max()'); if get_max() = d_MAX then pl('SUCCESS'); else
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 49 de 121

pl('ERROR: get_max() is not equal to d_MAX'); end if; pl('4. Testing get_min()'); if get_min() = d_MIN then pl('SUCCESS'); else pl('ERROR: get_min() is not equal to d_MIN'); end if; pl('5. Testing random() 1000 times'); for i in 1..1000 loop 22 d_date := random(1, 9999); end loop; pl('SUCCESS'); pl('6. Testing start_of_day()'); if to_char(start_of_day(SYSDATE), 'HH24MISS') = '000000' then pl('SUCCESS'); else pl('ERROR: start_of_day is not 00:00:00'); end if; end test; end DATES; / @/home/jamslug/date-body.sql

Ejercicio:compileloscdigosdelaespecificacinyelcuerpodelpaquete,luegoescribaun bloqueannimoenPL/SQLquepermitaejecutarelprocedimientotest. SolucinPropuesta:


begin DATES.test; end; .
22 For Loop permite definir un ciclo con un nmero predeterminado de ejecuciones, para este caso puntual se est definiendo un ciclo que se ejecutar 1000 veces. La sintaxis general de esta instruccin es: FOR loop_counter IN [REVERSE] lowest_number..highest_number LOOP {.statements.} END LOOP;
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 50 de 121

4.1EmpleandoCASEparacontrolarelflujodeunPrograma: Yasehavistohastaestapartelaformadecontrolarelflujodeunaaplicacinempleandolas instruccionesIFTHEN,IFELSETHEN,eIFTHENELSIFTHENELSE.Tambinesposible controlarelflujodeunprogramaempleandolainstruccinCASE. LaestructurabsicadeunasentenciaCASEeslasiguiente: CASESELECTOR WHENEXPRESSION1THENSTATEMENT1; WHENEXPRESSION2THENSTATEMENT2; ... WHENEXPRESSIONNTHENSTATEMENTN; ELSESTATEMENTN+1; ENDCASE; Ejercicio1:haciendousodelasinstruccionesIFELSETHENescribaunafuncinquereciba unnmeroydeterminesisetratadeunnmeroparoimpar.Ustedpuedesabersiun nmeroesparsialdividirlopordoselresiduodelaoperacines0,enOracleustedpuede emplearlafuncinMOD(m,n)paradeterminarelresiduodedividirmentren
CREATE OR REPLACE FUNCTION par_impar(v_numero IN NUMBER) RETURN VARCHAR2 IS BEGIN IF(v_numero>0) THEN IF23(MOD(v_numero,2)!=0) THEN RETURN 'impar'; ELSE RETURN 'par'; END IF; ELSE RETURN 'esta funcion solo sirve con enteros mayores a 0'; END IF; END; . /
23 Observe como este IF se encuentra dentro de un primer IF, a esto se le conoce con el nombre de IF anidados
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 51 de 121

@/home/jamslug/par_o_impar_1.sql

UnidaddePrueba(TestUnit)paraelejemploanterior:
SELECT par_impar(4) FROM dual;

1. Quocurresiingresa5enlugarde4? 2. Quocurresiingresa0ounnmeronegativo? Ejercicio2:reescribalafuncinanteriorempleandoCASEenlugardeIFELSETHEN


CREATE OR REPLACE FUNCTION par_impar(v_numero IN NUMBER) RETURN VARCHAR2 IS residuo NUMBER := MOD(v_numero,2); BEGIN CASE residuo WHEN 0 THEN RETURN 'par'; ELSE RETURN 'impar'; END CASE; END; . / @/home/jamslug/par_o_impar_2.sql

Quunidaddepruebapuedeemplearparaverificarestafuncin? 4.2CASEconcondicionesdebsqueda: Esposibletambinemplearelcasesobrebsquedasquedevuelvenresultadosbooleanos: TRUE,FALSE,NULL.Enestecasolasintaxisaempleareslasiguiente: CASE WHENSEARCHCONDITION1THENSTATEMENT1; WHENSEARCHCONDITION2THENSTATEMENT2; ... WHENSEARCHCONDITIONNTHENSTATEMENTN;


Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 52 de 121

ELSESTATEMENTN+1; ENDCASE; Ejercicio3:reescribalafuncinanteriorempleandounCASEconcondicionesdebsqueda.


CREATE OR REPLACE FUNCTION par_impar(v_numero IN NUMBER) RETURN VARCHAR2 IS BEGIN CASE WHEN (MOD(v_numero,2)=0) THEN --si devuelve TRUE RETURN 'par'; ELSE RETURN 'impar'; END CASE; END; . /

4.3ControldeIteracionesconLOOP: Enocasionessedeseeaqueunprogramarealicevariasveceslamismatarea,paralograr estodebendefinirseLOOPS.EnOraclehaycuatrotiposdiferentesdeLOOPS,seexpondrn acontinuacinlos3primeros. 4.3.1LoopSimple:enprincipioelLOOPmassimplequesepuededeclarartieneestaforma:


LOOP STATEMENT 1; STATEMENT 2; ... STATEMENT N; END LOOP;

Sinembargo,definidodeestamanera,elLOOPnoacabaradeiterarjams.Esnecesario incluirunacondicinqueindiquelasalidadelLOOP:
LOOP STATEMENT 1; STATEMENT 2; IF CONDITION THEN
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 53 de 121

EXIT; END IF; END LOOP;

Ejemplo1:escribaunbloqueannimoPL/SQLquerealicelassiguientestareas 1. construyaunatabladenominadaPruebaconlascolumnasID(NUMBER)y ALEATORIO(NUMBER) 2. definaunasecuenciaseq_pruebaquecomienceen1yseincrementaen1 3. definaunLOOPsimplequeinserte100registrosenlatabla,emplealasecuencia creadaenelpunto2paraobtenerelIDdecadanuevoregistro.Elvaloraleatoriopor otraparte,loobtendrconlafuncinvalue(n1,n2)delpaqueteDBMS_RANDOM

SolucinPropuesta:
DROP TABLE PRUEBA; CREATE TABLE PRUEBA( ID NUMBER, ALEATORIO NUMBER); ALTER TABLE PRUEBA ADD CONSTRAINT PRUEBA_PK PRIMARY KEY(ID); DROP SEQUENCE SEQ_PRUEBA; CREATE SEQUENCE SEQ_PRUEBA MINVALUE 1 START WITH 1 INCREMENT BY 1; DECLARE v_n1 CONSTANT NUMBER := 1; v_n2 CONSTANT NUMBER := 250; v_aux NUMBER; BEGIN v_aux := 0; LOOP INSERT INTO PRUEBA VALUES(SEQ_PRUEBA.NEXTVAL,DBMS_RANDOM.value(v_n1, v_n2)); v_aux := v_aux+1; IF(v_aux = 100) THEN
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 54 de 121

EXIT; END IF; END LOOP; END; . /

ElmismoejemplosepodramodificarligeramenteparaemplearlasintaxisdeEXITWHEN enlugardeEXITdelasiguientemanera:
DROP TABLE PRUEBA; CREATE TABLE PRUEBA( ID NUMBER, ALEATORIO NUMBER); ALTER TABLE PRUEBA ADD CONSTRAINT PRUEBA_PK PRIMARY KEY(ID); DROP SEQUENCE SEQ_PRUEBA; CREATE SEQUENCE SEQ_PRUEBA MINVALUE 1 START WITH 1 INCREMENT BY 1; DECLARE v_n1 CONSTANT NUMBER := 1; v_n2 CONSTANT NUMBER := 250; v_aux NUMBER; BEGIN v_aux := 0; LOOP INSERT INTO PRUEBA VALUES(SEQ_PRUEBA.NEXTVAL,DBMS_RANDOM.value(v_n1, v_n2)); v_aux := v_aux+1; EXIT WHEN (v_aux = 100); END LOOP; END; .
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 55 de 121

4.3.2WHILELOOPS:lasegundaclasedeLOOPSdisponiblessedenominaWHILE,su estructurabsicaeslasiguiente: WHILECONDITIONLOOP STATEMENT1; STATEMENT2; ... STATEMENTN; ENDLOOP; LadiferenciafundamentalconlosLOOPSsimplesesquelacondicinparacontinuar iterandoseevalaantesdecomenzarlaiteracin. EsposibleincluirlascondicionesEXITyEXITWHENenlosWHILELOOPconelfinde introducirsalidasprematurasenelmismo: WHILETEST_CONDITIONLOOP STATEMENT1; STATEMENT2; IFEXIT_CONDITIONTHEN EXIT; ENDIF; ENDLOOP; Ejemplo1:escribaunbloquedecdigoPL/SQLquerealicelassiguientestareas: 1. construyaunatabladenominadaPruebaconlascolumnasIDyALEATORIO 2. definaunasecuenciaseq_pruebaquecomienceen1yseincrementaen1 3. definaunLOOPsimplequeinserte100registrosenlatabla,emplealasecuencia creadaenelpunto2paraobtenerelIDdecadanuevoregistro.Elvaloraleatoriopor otraparte,loobtendrconlafuncinvalue(n1,n2)delpaqueteDBMS_RANDOM.Esta vezinsertarnicamentevaloresenteros,empleandoparatalfinlafuncin ROUND(nmero,precisin).Sienalgnmomentoseinsertaunnmeroqueyahaba sidoinsertadoantesseterminaprematuramentelaejecucindelcdigo. SolucinPropuesta:
DROP TABLE PRUEBA; CREATE TABLE PRUEBA( ID NUMBER,
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 56 de 121

ALEATORIO NUMBER); ALTER TABLE PRUEBA ADD CONSTRAINT PRUEBA_PK PRIMARY KEY(ID); DROP SEQUENCE SEQ_PRUEBA; CREATE SEQUENCE SEQ_PRUEBA MINVALUE 1 START WITH 1 INCREMENT BY 1; DECLARE v_n1 CONSTANT NUMBER := 1; v_n2 CONSTANT NUMBER := 250; v_aux NUMBER; v_verify1 NUMBER; v_verify2 NUMBER; BEGIN v_aux := 0; WHILE (v_aux < 100) LOOP v_verify1 := round(DBMS_RANDOM.value(v_n1, v_n2),0); INSERT INTO PRUEBA VALUES(SEQ_PRUEBA.NEXTVAL,v_verify1); v_aux := v_aux+1; SELECT COUNT(ID) INTO v_verify2 FROM PRUEBA WHERE ALEATORIO = v_verify1; EXIT WHEN (v_verify2 > 1); END LOOP; END; . /

Ejercicio1:escribaunprocedimientoPL/SQLquecalculelasumadelosnmerosdel1al 10yvayamostrandoacadapasoelvalordelasumaparcial.Amaneradeejemplose presentanlasprimeraslneasquedebemostrarenpantallaesteprocedimiento: 1 3


Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 57 de 121

6 10 15 ... SolucinPropuesta:
CREATE OR REPLACE PROCEDURE sumar1_10 IS var_sumatoria NUMBER := 0; var_contador NUMBER := 0; BEGIN WHILE(var_contador < 10) LOOP var_contador := var_contador + 1; var_sumatoria := var_sumatoria + var_contador; pl(var_sumatoria); END LOOP; END; . /

Ejercicio2:escribalaunidaddepruebaparaelprocedimientosumar1_10 SolucinPropuesta:
begin sumar1_10; end; . /

4.3.3FORLOOP:estaterceraclasedeLOOPsedenominannumricospuesrequierenun valordeestetipoparasuterminacin.Laestructurabsicadelosmismoseslasiguiente: FORloop_counterIN[REVERSE]lower_limit..upper_limitLOOP STATEMENT1; STATEMENT2; ... STATEMENTN; ENDLOOP;

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 58 de 121

Ejemplo1:escribaunaunidaddepruebaparalafuncinpreviamentecreadapar_impar quehaciendousodeunFORLOOPrealice100pruebasaleatoriasdelafuncineimprima susresultadosenpantalla. SolucinPropuesta:


DECLARE v_aux NUMBER; BEGIN FOR i IN 1..100 LOOP v_aux := round(DBMS_RANDOM.value(1,1000),0); pl(v_aux||':'||par_impar(v_aux)); END LOOP; END; . / @/home/jamslug/unit_test_for_loop.sql

ThereisnoneedtodefinetheloopcounterinthedeclarationsectionofthePL/SQLblock. Thisvariableisdefinedbytheloopconstruct.lower_limitandupper_limitaretwointeger numbersorexpressionsthatevaluatetointegervaluesatruntime,andthedoubledot(..) servesastherangeoperator. Ejercicio1:escribaunafuncinquepermitacalcularelfactorialdeunnmero.Recuerde queelfactorialde5sedefinecomo5!=1*2*3*4*5 SolucinPropuesta:


CREATE OR REPLACE FUNCTION factorial(p_numero IN NUMBER) RETURN NUMBER IS v_resultado NUMBER := 1; BEGIN FOR i IN 1..p_numero LOOP v_resultado := v_resultado*i; END LOOP; RETURN v_resultado; END; . / @/home/jamslug/factorial.sql

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 59 de 121

4.3.4LoopsAnidados:AligualquelosoperadoresdecomprobacinIFELSEylosbloques decdigoPL/SQL,losLOOPSpuedenanidarseunosdentrodeotrosparaformarestructuras lgicasmscomplejas,comopuedeverseenelsiguienteejemplo. Ejemplo1:simuladordecalificaciones 1. Creeunatabladenominadasimulaconlasiguienteestructura:ID(Number),Codigo (Number),Calificacion(Number) 2. Definaunasecuenciaseq_simulaquecomienceen1yseincrementeen1 3. EscribaunbloqueannimoPL/SQLquerealicelosiguiente10veces: 1. Inserte1registroenlatablaconelvalordelIDobtenidodelasecuencia,elCdigo comounnmeroaleatorioredondeadoa0decimalesentreel19989070yel 79989070yunaCalificacincomounnmeroaleatorioredondeadoa2decimales entreel1yel5. 2. Repitaesteprocedimiento4vecesconservandoelCODIGOanteriordeformaque paraunmismoalumnosetengan5calificaciones 4. Presenteunreportedelascalificacionesobtenidas SolucinPropuesta:
DROP TABLE SIMULA; CREATE TABLE SIMULA( ID NUMBER, CODIGO NUMBER, CALIFICACION NUMBER); DROP SEQUENCE SEQ_SIMULA; CREATE SEQUENCE SEQ_SIMULA MINVALUE 1 START WITH 1 INCREMENT BY 1 CACHE 50; --BLOQUE PL/SQL SOLICITADO DECLARE aux_codigo NUMBER := 0; BEGIN FOR i IN 1..10 LOOP --loop externo aux_codigo := ROUND(DBMS_RANDOM.VALUE(19989070,79989070),0); INSERT INTO SIMULA VALUES(SEQ_SIMULA.NEXTVAL,aux_codigo,ROUND(DBMS_RANDOM.VALUE(1,5),2)); FOR j IN 1..4 LOOP --loop interno INSERT INTO SIMULA
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 60 de 121

VALUES(SEQ_SIMULA.NEXTVAL,aux_codigo,ROUND(DBMS_RANDOM.VALUE(1,5),2)) ; END LOOP; --loop interno END LOOP; --loop externo END; . /

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 61 de 121

5.ParmetrosdeEntradaySalidaAclaracin: Hastaelmomentolosparmetrosempleadosenlosdiferentesprocedimientosyfunciones hansidodeentrada,esdecirvaloresqueelusuarioingresaalmomentodellamaral procedimientoofuncinyquesonusadosalinteriordelmismoparaproducirunresultado. Losparmetrosdesalida(OUT)sonaquelloscuyovalornoesaccesiblealinteriordel procedimientoofuncinalcualsepasan,peroquepuedensermodificadosdesdeelmismo. LosparmetrosINOUT(deentradaysalida)sonaquelloscuyovalorseencuentradisponible alinteriordelafuncinoprocedimientoquelosllamayquepuedensermodificadosdesdeel mismo.Elsiguienteejemploservirparaaclararesteconcepto. Ejemplo1:EscribaunpaquetedenominadoPARAMETERSquecuenteconlassiguientes funcionesyprocedimientos. 1. FUNCTIONname_to_upper(ain_nameINVARCHAR2)RETURNVARCHAR2,esta funcinretornaelvalordeain_nameenmaysculasusandolafuncinupperparatal fin 2. PROCEDUREname_to_upper2(aout_nameOUTVARCHAR2),esteprocedimiento modificaelvalordelavariableinternadelpaquetedefiniendoparalamismaunnuevo valorenmaysculas 3. PROCEDUREname_to_lower(ainout_nameINOUTVARCHAR2),esteprocedimiento tomaelvaloractualdelavariableinternadelpaqueteylomodificadejndoloen minsculas,paralocualemplealafuncinlower SolucinPropuesta: EspecificacindelPaquete:
--ejemplo del alcance de las variables usando los --identificadores IN OUT --archivo de especificacion del paquete PARAMETERS CREATE OR REPLACE PACKAGE PARAMETERS AS FUNCTION name_to_upper(ain_name IN VARCHAR2) RETURN VARCHAR2; PROCEDURE name_to_upper2(aout_name OUT VARCHAR2); PROCEDURE name_to_lower(ainout_name IN OUT VARCHAR2); --TEST UNIT PROCEDURE test;

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 62 de 121

END PARAMETERS; . /

CuerpodelPaquete:
CREATE OR REPLACE PACKAGE BODY PARAMETERS AS v_name VARCHAR(120):='Jack Daniels'; FUNCTION name_to_upper(ain_name IN VARCHAR2) RETURN VARCHAR2 IS BEGIN RETURN ('valor devuelto por la funcion: '||upper(ain_name)); END name_to_upper; PROCEDURE name_to_upper2(aout_name OUT VARCHAR2) IS --aunque nada se muestre en pantalla la variable --interna del paquete est siendo modificada BEGIN aout_name := upper('Casper Houser'); END name_to_upper2; PROCEDURE name_to_lower(ainout_name IN OUT VARCHAR2) IS --aunque nada se muestre en pantalla la variable --interna del paquete est siendo modificada BEGIN ainout_name := lower(ainout_name); END name_to_lower; PROCEDURE test IS BEGIN pl('---- UNIDAD DE PRUEBA PAQUETE: PARAMETERS ----'); pl('valor incial de la variable v_name: '||v_name); pl('llamando a la funcion name_to_upper...'); pl(name_to_upper(v_name));
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 63 de 121

pl('valor de la variable v_name luego de llamar a la funcion: '|| v_name); pl('llamando al procedimiento name_to_upper2...'); name_to_upper2(v_name); pl('valor de la variable v_name luego de llamar al procedimiento: '|| v_name); pl('llamando al procedimiento name_to_lower...'); name_to_lower(v_name); pl('valor de la variable v_name luego de llamar al procedimiento: '|| v_name); pl('---- fin de la unidad de prueba ----'); END; END PARAMETERS; . /

Ejercicio1:compileelpaqueteparametersyejecutesuprocedimientodepruebamediante lainstruccinEXECUTEPARAMETERS.test,analicelasalidarecuerdedefinirlavariable SERVEROUTPUTenON

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 64 de 121

6.ManejodeExcepciones: Paradesarrollarestaparteemplearemosunafuncindivision(n1,n2)quedevolverelvalor dedividirn1entren2.Elcdigodelamismaeselsiguiente:


--division.sql CREATE OR REPLACE FUNCTION DIVISION(N1 IN NUMBER, N2 IN NUMBER) RETURN NUMBER IS BEGIN RETURN(N1/N2); END; . /

ParaprobarlafuncinpodemosemplearelsiguienteTestUnit:SELECT DIVISION(8,2) FROM DUAL;Sinembargoaunquelafuncinrealizasutareasininconvenientequocurresi unusuariointentaesteTestUnit?:SELECT DIVISION(8,0) FROM DUAL;Aunquelafuncin estescritacorrectamenteseproduceunacondicindeterminacinprematuradebidaal hechodequenoesposibledividirunnmeropor0:
ERROR at line 1: ORA-01476: divisor is equal to zero ORA-06512: at "SYSTEM.DIVISION", line 5

Debeentoncesmanejarseesaexcepcin:
--division.sql CREATE OR REPLACE FUNCTION DIVISION(N1 IN NUMBER, N2 IN NUMBER) RETURN NUMBER IS BEGIN RETURN(N1/N2); EXCEPTION WHEN ZERO_DIVIDE THEN pl('No es posible divir por 0'); END; . /

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 65 de 121

Ahoracuandoseejecutalamismaunidaddepruebaquegenerlaterminacinprematura anteriorseobservalasiguientesalida:
ERROR at line 1: ORA-06503: PL/SQL: Function returned without value ORA-06512: at "SYSTEM.DIVISION", line 11 No es posible divir por 0

Laexcepcinestsiendocontrolada,elerrorqueaparecesedebeaqueunafuncindebe retornarunvaloryenestecasonoloesthaciendo,porloquepodramodificarseelcdigo paradevolverunvalornumricoqueindiqueelerror:


--division.sql CREATE OR REPLACE FUNCTION DIVISION(N1 IN NUMBER, N2 IN NUMBER) RETURN NUMBER IS BEGIN RETURN(N1/N2); EXCEPTION WHEN ZERO_DIVIDE THEN RETURN -1; --pl('No es posible divir por 0'); END; . /

6.1ExcepcionesComunesIncluidasenOracle: ExistendiferentesexcepcionesincluidasenOraclequepermitencontrolarlascondicionesde errormscomunes,lasiguientetablapresentaunresumendeestas: Excepcin NO_DATA_FOUND Origen Estaexcepcinsepresenta cuandoalrealizaruna consulta(SELECToSELECT INTO)noseobtieneningn valor Observacin EstaexcepcinNOocurre cuandoserealizaunSELECT COUNToSELECTSUM, porquecualquieradeestas dosfuncionesretornaun0en casodenoexistirregistros

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 66 de 121

TOO_MANY_ROWS

Aparececuandoalrealizar unaconsultaconSELECT INTOelresultadocontiene msdeunafila Aparecealintentaruna divisinpor0 Ocurrecuandounusuario intentaconectarseconel servidorOracleempleando unnombredeusuarioy/ouna contraseainvlidos Aparececuandounprograma PL/SQLcontieneunerror Ocurrecuandounavariable nopuedeconvertirsedeun tipoaotro,ocuandoel tamaodelamismanole permitealmacenarelvalor solicitado Ocurrecuandoseintenta insertarunvalorduplicadoen unacolumnaconrestriccin devalornico

ZERO_DIVIDE LOGIN_DENIED

PROGRAM_ERROR VALUE_ERROR

DUP_VALUE_ON_INDEX

Ejemplo1:inserteenlatablaSIMULArealizadaenelejercicioanteriorunnuevovalor, empleelasecuenciaparaobtenerelID,elvalor79799331comoCODIGOyla CALIFICACION2. EscribaunprocedimientoCONSULTAR_ALUMNOquerecibacomoparmetrodeentrada (IN)elcdigodeunalumnoytraigasucalificacin.Elprocedimientodebemanejar2 excepciones: 1. NO_DATA_FOUNDparaelcasoenelqueserealiceunaconsultaporuncdigo inexistente 2. TOO_MANY_ROWSparaelcasoenqueserealicelaconsultaparaunusuarioque tengamsdeunacalificacinesdecircualquieradeloscdigosexistentesenla tablaSIMULAaexcepcindel79799331queseinsertalcomenzaresteejemplo

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 67 de 121

SolucinPropuesta:
-- insercion del registro solicitado INSERT INTO SIMULA VALUES(SEQ_SIMULA.NEXTVAL,79799331,2); --Procedimiento consultar_alumno CREATE OR REPLACE PROCEDURE CONSULTAR_ALUMNO(AIN_CODIGO IN NUMBER) IS V_CAL NUMBER := 0; BEGIN SELECT S.CALIFICACION INTO V_CAL FROM SIMULA S WHERE CODIGO = AIN_CODIGO; pl('La calificacion solicitada es: '||V_CAL); --manejo de excepciones EXCEPTION WHEN NO_DATA_FOUND THEN pl('No existen calificaciones para el Codigo: '||AIN_CODIGO); WHEN TOO_MANY_ROWS THEN pl('Existe mas de 1 calificacion para el Codigo: '||AIN_CODIGO); END; . /

Ejercicio2:escribalastressentenciasSQLconlasqueprobaralosdiferentes comportamientosdelprocedimientoanterior,consultelatablaSIMULAprimeroparasaber queparmetrosusarencadacaso. SolucinPropuestadependedelosdatosdelatablaSIMULA:


SQL> EXECUTE CONSULTAR_ALUMNO(79799331); La calificacion solicitada es: 2 PL/SQL procedure successfully completed. SQL> EXECUTE CONSULTAR_ALUMNO(46135886); Existe mas de 1 calificacion para el Codigo: 46135886 PL/SQL procedure successfully completed.
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 68 de 121

SQL> EXECUTE CONSULTAR_ALUMNO(007); No existen calificaciones para el Codigo: 7 PL/SQL procedure successfully completed.

Esposiblemodificarligeramenteelcdigodelejemploanterioryaadirunanuevaexcepcin paraelcasoenqueocurraunerrorquenosehaconsideradoyobtenermsinformacin acercadelmismo:


--Procedimiento consultar_alumno CREATE OR REPLACE PROCEDURE CONSULTAR_ALUMNO(AIN_CODIGO IN NUMBER) IS V_CAL NUMBER := 0; BEGIN SELECT S.CALIFICACION INTO V_CAL FROM SIMULA S WHERE CODIGO = AIN_CODIGO; pl('La calificacion solicitada es: '||V_CAL); --manejo de excepciones EXCEPTION WHEN NO_DATA_FOUND THEN pl('No existen calificaciones para el Codigo: '||AIN_CODIGO); WHEN TOO_MANY_ROWS THEN pl('Existe mas de 1 calificacion para el Codigo: '||AIN_CODIGO); --otras excepciones que se puedan presentar WHEN OTHERS THEN raise_application_error(-20002, SQLERRM); END; . /

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 69 de 121

7.Cursores: Hastaelmomentotodaslasoperacionesdeconsultarealizadashantrabajadosobreuna nicafiladeresultados,enelcasoenqueexistemsdeunafilasehaimplementadoel manejodelaexcepcinTOO_MANY_ROWSparaevitarlaterminacinprematuradelos programas.SinembargoPL/SQLproveelasherramientasnecesariasparatrabajarcon gruposderegistros,esaherramientarecibeelnombredeCURSOR.Unadefinicinms formaldeCURSOReslasiguiente: CursorsarememoryareaswhereOracleexecutesSQLstatements.Indatabase programmingcursorsareinternaldatastructuresthatallowprocessingofSQLqueryresults. ExistendosclasesdeCURSORSenOracle,losimplcitosylosexplcitos.Losprimerosson generadosdeformaautomticaporlabasededatoscadavezqueunsentenciaSQLse ejecuta,lossegundossondefinidosporelprogramadordentrodesucdigoPL/SQLquien poseecontrolsobreellos.Parapoderentenderloscursoresexplcitosesnecesarioprimero comprenderunpocomejorelfuncionamientodeloscursoresimplcitos: 1. TodoslosbloquesPL/SQLgeneranunCURSORimplcitocadavezqueejecutanuna sentenciaSQL 2. UnCURSORseasociademaneraautomticaconcadaoperacindemanipulacin dedatos(DML):UPDATE,SELECT,INSERT 3. LasoperacionesUPDATEyDELETEcuentanconunCURSORasociadoquepermite obtenerinformacinacercadelgrupodefilasafectadoporlaoperacin 4. Todaslassentenciasdeinsercin:INSERTnecesitanunlugardondealmacenar temporalmentelosdatosquedeberningresarenlabasededatos.ElCURSORes eselugar 5. ElltimoCURSORabiertorecibeelnombredeSQLCURSOR Ejercicio1:parapoderverenaccinaloscursoresimplcitosesnecesariocrearunnuevo procedimientoquetrabajarsobrelatablaSIMULAcreadaenunejemploanterior.El procedimientosellamarACTUALIZAR_ALUMNO,recibircomoparmetrodeentradaun Cdigoyactualizar(medianteunasentenciaUPDATEdeSQL)lasCalificacionesasociadas adichoCdigosumndole1,siempreycuandoelresultadoobtenidoseamenoroiguala5.

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 70 de 121

SolucinPropuesta:
--Procedimiento actualizar_alumno CREATE OR REPLACE PROCEDURE ACTUALIZAR_ALUMNO(AIN_CODIGO IN NUMBER) IS BEGIN UPDATE SIMULA SET CALIFICACION = CALIFICACION+1 WHERE CODIGO = AIN_CODIGO AND ((CALIFICACION+1)<=5); --manejo de excepciones EXCEPTION WHEN NO_DATA_FOUND THEN pl('No existen calificaciones para el Codigo: '||AIN_CODIGO); --otras excepciones que se puedan presentar WHEN OTHERS THEN raise_application_error(-20002, SQLERRM); END; . /

Puedemodificarseligeramenteelcdigoanteriorparaincluiruncursorqueinformeacerca delnmerodecalificacionesmodificadas:
--Procedimiento actualizar_alumno CREATE OR REPLACE PROCEDURE ACTUALIZAR_ALUMNO(AIN_CODIGO IN NUMBER) IS BEGIN UPDATE SIMULA SET CALIFICACION = CALIFICACION+1 WHERE CODIGO = AIN_CODIGO AND ((CALIFICACION+1)<=5); --consulta del CURSOR implicito para saber cuantas calificaciones --fueron actualizadas pl('# de calificaciones actualizadas: '||SQL%ROWCOUNT);--SQL es el nombre del CURSOR --manejo de excepciones EXCEPTION WHEN NO_DATA_FOUND THEN pl('No existen calificaciones para el Codigo: '||AIN_CODIGO);
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 71 de 121

--otras excepciones que se puedan presentar WHEN OTHERS THEN raise_application_error(-20002, SQLERRM); END; . /

Ejercicio2:pruebeelfuncionamientodelanteriorprocedimientomediantelasentencia: execute actualizar_alumno(<codigo_que_se_encuentre_en_su_tabla>);Ejecutela pruebavariasveces,amedidaquelascalificacionesdelusuariovayanincrementndose ustednotarqueelnmeroderegistrosactualizadosesmenor.

7.1DefinicindeunCURSORexplcito: LoscursoresexplcitossedefinenenlaseccindeDECLARACIONdeunprogramaPL/SQL, unavezdeclaradossetrabajaconellosdelasiguientemanera: 1. Abrirelcursor(open) 2. Invocarelcursor(fetch) 3. Cerrarelcursor(close) LadeclaracindeunCURSORdefinesunombreylasentenciaSELECTdeSQLasociadaal mismo,lasintaxisgeneraldedeclaracineslasiguiente:


CURSOR c_cursor_name IS select statement

Ejemplo1:definauncursorquetraigatodaslascalificacionesasociadasauncdigodado
DECLARE CURSOR c_MyCursor IS SELECT CALIFICACION FROM SIMULA WHERE CODIGO = 5665789;

7.1.1Records: UnRECORDesuntipodedatocompuesto,puedeestarasociadoaunatabla,aun CURSORoserdefinidodirectamenteporelprogramador.

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 72 de 121

ParadeclararunRecordbasadoenunatablaseemplealasiguientesintaxis:
record_name table_name%ROWTYPE

SisedeseaqueelRecordestbasadoenunCURSORseemplealasiguientesintaxis:
record_name cursor_name%ROWTYPE

Ejemplo1:EscribaunfragmentoPL/SQLenelquesedeclareunRECORDasociadoala tablaSIMULAyempleoluegodentrodeunasentenciaSELECTINTO.
--declaracion y uso basico de un RECORD basado en TABLA DECLARE vr_infoalumno SIMULA%ROWTYPE; BEGIN SELECT * FROM SIMULA INTO vr_infoalumno WHERE CODIGO = 79799331;

UstedpuedeentoncesusardeformaconjuntaunCURSORyunRECORDparaprocesar conjuntosdedatosenlugardedatosindependientes. Ejemplo2:EscribaunbloquePL/SQLquecumplalassiguientestareas: 1. DefinaunCURSORquerecuperartodoslosregistrosdelatablaSIMULAparaun CODIGOespecfico 2. DEFINAUNRECORDasociadoalatablaSIMULAenelquepuedairseprocesandola informacinreferenciadaporelCURSORanterior 3. Presenteenpantallacadaunodelosregistrosobtenidos SolucinPropuesta:


--ejemplo basico de trabajo con CURSORES explicitos DECLARE CURSOR c_grades IS --declaracion del CURSOR SELECT * FROM SIMULA WHERE CODIGO = 46135886; vr_info SIMULA%ROWTYPE; --declaracion de un record del tipo tabla: SIMULA BEGIN OPEN c_grades; --1. Abrir el Cursor LOOP

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 73 de 121

FETCH c_grades INTO vr_info; --2. Recuperar la informacion del CURSOR EXIT WHEN(c_grades%NOTFOUND); --Condicion para salir del LOOP: no hay mas registros pl(vr_info.ID||' '||vr_info.CODIGO||' '||vr_info.CALIFICACION); END LOOP; CLOSE c_grades; --3. Cerrar el Cursor END; . /

Ejercicio1:compileelanteriorbloquePL/SQLempleandounCODIGOqueseencuentreen sutablaSIMULA,ejecteloyanalicelosresultadosobtenidos. Ejemplo3:modifiqueelejemploanteriorparaquelaconsultaslorecupereloscamposIDy CALIFICACION.


--ejemplo basico de trabajo con CURSORES explicitos DECLARE CURSOR c_grades IS --declaracion del CURSOR SELECT ID,CALIFICACION FROM SIMULA WHERE CODIGO = 46135886; vr_info c_grades%ROWTYPE24; --declaracion de un record del tipo CURSOR BEGIN OPEN c_grades; --1. Abrir el Cursor LOOP FETCH c_grades INTO vr_info; --2. Recuperar la informacion del CURSOR EXIT WHEN(c_grades%NOTFOUND); --Condicion para salir del LOOP: no hay mas registros pl(vr_info.ID||' '||vr_info.CALIFICACION); END LOOP; CLOSE c_grades; --3. Cerrar el Cursor END; . /

Ejercicio2:compileelanteriorbloquePL/SQLempleandounCODIGOqueseencuentreen sutablaSIMULA,ejecteloyanalicelosresultadosobtenidos.

24 Al declarar un RECORD del mismo tipo del CURSOR se cuenta con un contenedor idneo para la informacin que ser recuperada de este ltimo.
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 74 de 121

Ejercicio3:ahoraqueconoceelconceptodeCURSORSyRECORDSpuedemodificarel cdigodelprocedimientoalmacenadoCONSULTAR_ALUMNO,paraqueenlugarde capturarunaexcepcincuandohaymsdeunacalificacinporalumno,presentelas calificacionesdelosalumnossiempreycuandoelcdigoingresadoseencuentreenlabase dedatos. SolucinPropuesta:


--Procedimiento consultar_alumno CREATE OR REPLACE PROCEDURE CONSULTAR_ALUMNO2(AIN_CODIGO IN NUMBER) IS CURSOR c_infoalumno IS SELECT * FROM SIMULA WHERE CODIGO = AIN_CODIGO; vr_alumnocal c_infoalumno%ROWTYPE; --debe funcionar igual si se usa SIMULA%ROWTYPE BEGIN OPEN c_infoalumno; --1.Open Cursor pl('--- CALIFICACIONES DISPONIBLES PARA EL CODIGO: '||AIN_CODIGO); LOOP FETCH c_infoalumno INTO vr_alumnocal; --2.Fetch Cursor EXIT WHEN(c_infoalumno%NOTFOUND); pl('ID: '||vr_alumnocal.ID||' CALIFICACION: '|| vr_alumnocal.CALIFICACION); END LOOP; CLOSE c_infoalumno; --3.Close Cursor --manejo de excepciones EXCEPTION WHEN NO_DATA_FOUND THEN pl('No existen calificaciones para el Codigo: '||AIN_CODIGO); --otras excepciones que se puedan presentar WHEN OTHERS THEN raise_application_error(-20002, SQLERRM); END; . /

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 75 de 121

Ejercicio4:quocurreconelprocedimientoanteriorcuandoseingresauncdigoqueno existeenlatabla? 7.2AtributosdelosCURSORES: LossiguientessonlosatributosdeloscursoresquepuedenusarseenunprogramaPL/SQL Nombre %NOTFOUND FormadeUso nombrecursor%NOTFOUND Descripcin EsteatributodevuelveTRUE silaoperacinFETCH inmediatamenteanteriorNO recuperunregistrovlidoo FALSEcuandoesposible hacerlo EsteatributodevuelveTRUE silaoperacinFETCH inmediatamenteanterior recuperunregistrovlidoo FALSEcuandoNOesposible hacerlo

%FOUND

nombrecursor%FOUND

%ROWCOUNT

nombrecursor%ROWCOUNT Permiteconocerelnmerode registrosquehansido recuperadosconlas operacionesFETCH anteriores nombrecursor%ISOPEN DevuelveTRUEsielcursor consultadoseencuentra abierto,FALSEencaso contrario

%ISOPEN

Ejercicio5:haciendousodelosatributoscomunesdelosCURSORESytrabajandosobrela tablaSIMULAcomplementeelprocedimientoCONSULTAR_ALUMNO2paraquerealicelas siguientestareas: 1. EvaleelnmerodecalificacionesdisponiblesparaelCODIGOsolicitadoyloimprima enpantallaenlamismalneaenlaqueestimprimiendoelcdigo 2. EmpleeunWHILELOOPhaciendousodelatributo%FOUNDenlugardelEXIT WHENqueestutilizandohastaelmomento 3. Verifiquesielcursorseencuentraabiertoantesdecerrarlolocualporsupuestoser asimprimaunmensajeadvirtiendoestehecho 4. Cierreelcursoryvuelvaaverificarsiseencuentraabierto,imprimaunmensaje
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 76 de 121

advirtiendoelnuevoestadodelcursor SolucinPropuesta:
--Procedimiento consultar_alumno CREATE OR REPLACE PROCEDURE CONSULTAR_ALUMNO3(AIN_CODIGO IN NUMBER) IS CURSOR c_infoalumno IS SELECT * FROM SIMULA WHERE CODIGO = AIN_CODIGO; vr_alumnocal c_infoalumno%ROWTYPE; v_lastid NUMBER := 0; BEGIN pl('--- CALIFICACIONES DISPONIBLES PARA EL CODIGO: '||AIN_CODIGO); OPEN c_infoalumno; --1.Open Cursor FETCH c_infoalumno INTO vr_alumnocal; --2.Fetch Cursor - la primera vez fuera del loop pl('ID: '||vr_alumnocal.ID||' CALIFICACION: '|| vr_alumnocal.CALIFICACION); WHILE (c_infoalumno%FOUND) LOOP v_lastid := vr_alumnocal.ID; FETCH c_infoalumno INTO vr_alumnocal; --2.Fetch Cursor IF(v_lastid!=vr_alumnocal.ID) THEN -- evita que la ultima linea aparezca dos veces pl('ID: '||vr_alumnocal.ID||' CALIFICACION: '|| vr_alumnocal.CALIFICACION); END IF; END LOOP; pl('Total Calificaciones Obtenidas: '||c_infoalumno%ROWCOUNT); IF(c_infoalumno%ISOPEN) THEN pl('Estado del Cursor: Open'); ELSE pl('Estado del Cursor: Closed'); END IF;

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 77 de 121

CLOSE c_infoalumno; --3.Close Cursor IF(c_infoalumno%ISOPEN) THEN pl('Estado del Cursor: Open'); ELSE pl('Estado del Cursor: Closed'); END IF; --manejo de excepciones EXCEPTION WHEN NO_DATA_FOUND THEN pl('No existen calificaciones para el Codigo: '||AIN_CODIGO); --otras excepciones que se puedan presentar WHEN OTHERS THEN raise_application_error(-20002, SQLERRM); END; . /

Esposiblemodificarelcdigoanteriorparaincluirunaexcepcinqueselancecuandonose cierraelCURSOR:
--Procedimiento consultar_alumno CREATE OR REPLACE PROCEDURE CONSULTAR_ALUMNO3(AIN_CODIGO IN NUMBER) IS CURSOR c_infoalumno IS SELECT * FROM SIMULA WHERE CODIGO = AIN_CODIGO; vr_alumnocal c_infoalumno%ROWTYPE; --debe funcionar igual si se usa SIMULA%ROWTYPE v_lastid NUMBER := 0; BEGIN pl('--- CALIFICACIONES DISPONIBLES PARA EL CODIGO: '||AIN_CODIGO); OPEN c_infoalumno; --1.Open Cursor FETCH c_infoalumno INTO vr_alumnocal; --2.Fetch Cursor - la primera vez fuera del loop pl('ID: '||vr_alumnocal.ID||' CALIFICACION: '|| vr_alumnocal.CALIFICACION);
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 78 de 121

WHILE (c_infoalumno%FOUND) LOOP v_lastid := vr_alumnocal.ID; FETCH c_infoalumno INTO vr_alumnocal; --2.Fetch Cursor IF(v_lastid!=vr_alumnocal.ID) THEN -- evita que la ultima linea aparezca dos veces pl('ID: '||vr_alumnocal.ID||' CALIFICACION: '|| vr_alumnocal.CALIFICACION); END IF; END LOOP; pl('Total Calificaciones Obtenidas: '||c_infoalumno%ROWCOUNT); IF(c_infoalumno%ISOPEN) THEN pl('Estado del Cursor: Open'); ELSE pl('Estado del Cursor: Closed'); END IF; CLOSE c_infoalumno; --3.Close Cursor IF(c_infoalumno%ISOPEN) THEN pl('Estado del Cursor: Open'); ELSE pl('Estado del Cursor: Closed'); END IF; --manejo de excepciones EXCEPTION WHEN NO_DATA_FOUND THEN pl('No existen calificaciones para el Codigo: '||AIN_CODIGO); --otras excepciones que se puedan presentar WHEN OTHERS THEN IF(c_infoalumno%ISOPEN) THEN CLOSE c_infoalumno; --3.Close Cursor END IF; END; . /

Esmuyimportantecerrarsiempreloscursoresexplcitoslosimplcitossoncerradospor ORACLEautomticamentepuesdeestamaneraseliberanlosrecursosdememoria
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 79 de 121

asignadosacadaunodeellos,encasocontrariounprogramaenPL/SQLpodraconsumir demasiadosrecursosdeHardware,hastallegaralpuntodetumbarelservidor. 7.3CursorForLoop: EsposibleemplearunasintaxisalternativaconlosCURSORSquepermitemanejarde maneraimplcitalasoperacionesdeOPEN,FETCHYCLOSE.Aestasintaxisseleconoce comounCURSORFORLOOP Ejemplo1:reescribaelprocedimientoconsultaralumnoempleandounCursorForLoop SolucinPropuesta:


--Procedimiento consultar_alumno empleando un Cursor For Loop CREATE OR REPLACE PROCEDURE CONSULTAR_ALUMNO4(AIN_CODIGO IN NUMBER) IS CURSOR c_infoalumno IS SELECT * FROM SIMULA WHERE CODIGO = AIN_CODIGO; BEGIN pl('--- CALIFICACIONES DISPONIBLES PARA EL CODIGO: '||AIN_CODIGO); FOR vr_alumnocal25 IN c_infoalumno LOOP 26 pl('ID: '||vr_alumnocal.ID||' CALIFICACION: '|| vr_alumnocal.CALIFICACION); END LOOP; --manejo de excepciones EXCEPTION WHEN NO_DATA_FOUND THEN pl('No existen calificaciones para el Codigo: '||AIN_CODIGO); --otras excepciones que se puedan presentar WHEN OTHERS THEN IF(c_infoalumno%ISOPEN) THEN CLOSE c_infoalumno; --3.Close Cursor END IF; END; . /

25 Este RECORD se est definiendo implcitamente del mismo tipo que el CURSOR 26 Las operaciones OPEN, FETCH y CLOSE estn siendo manejadas implcitamente por el motor ORACLE
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 80 de 121

7.4ParmetrosparalosCursores: Aligualquelasfuncionesyprocedimientos,esposibledefinirunCURSORconparmetros, estopermiteunamayorflexibilidadconlosCURSORESyloshacemsreutilizables. Ejemplo1:EscribaunbloquedePL/SQLquesumelosfactorialesde2nmerosingresados porelusuario.Empleelafuncinfactorialquecreoenunejercicioanterior.


--Bloque PL/SQL que suma los factoriales de dos numeros DECLARE v_n1 NUMBER := &n1; v_n2 NUMBER := &n2; v_aux1 NUMBER := 0; v_aux2 NUMBER := 0; CURSOR c_factorial (vc_numero IN NUMBER) IS SELECT factorial(vc_numero) FROM DUAL; BEGIN OPEN c_factorial(v_n1);--OPEN cursor + PARAM FETCH c_factorial INTO v_aux1; --FECTH cursor CLOSE c_factorial; --CLOSE CURSOR OPEN c_factorial(v_n2);--OPEN cursor + PARAM FETCH c_factorial INTO v_aux2; --FECTH cursor CLOSE c_factorial; --CLOSE CURSOR v_aux2 := v_aux2 + v_aux1; pl(v_n1||'!+'||v_n2||'!='||v_aux2); END; . /

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 81 de 121

7.5Cursoresanidados: LoscursorespuedenanidarsealigualqueocurreconlosLOOPSquesevieronenlecciones anteriores.Elsiguienteejemploilustraestaidea. Ejemplo1:Considereelsiguientemodelo:

1. EscribaelcdigoSQLqueseencarguedegenerarestemodelo27enlabasededatos, tengaencuentalasllavesprimariasylasllavesforneas.Tengaencuentatambin queeldocumentodecadaestudiantesernico. 2. Creeunasecuenciaparalastablasquelonecesitan,elnombredeestassecuencias debeserSEQ_<nombre_tabla>.Estassecuenciascomenzarnen1yse incrementarnenvaloresde1.EscribaelcdigoSQL. 3. EscribaelcdigoSQLqueinsertelossiguientes5estudiantesenlatabla correspondiente: 1. JuanPerez79899012 2. RosaHernandez52780041 3. MaraCastro53900789 4. CarlosHernandez80001000 5. PedroMarin79998901 4. EscribaelcdigoSQLqueinserte5cursosenlatablacorrespondiente 1. Matematicas 2. Geometria 3. Biologia 4. BasesdeDatos 5. ProgramacionOrientadaaObjetos 5. EscribaunbloqueovariosPL/SQLqueinscribanalosestudiantesenlasmaterias
27 Todos los campos ID sern de tipo NUMBER, el campo DOCUMENTO_ESTUDIANTE ser de tipo VARCHAR2(20), el campo FECHA_EVALUACION de tipo DATE, el campo CALIFICACION de tipo NUMBER, los dems campos tipo VARCHAR2(50)
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 82 de 121

delasiguientemanera: 1. Encadamateriadebenquedarinscritoscomomnimodosestudiantescomo mximo4(sugerenciaempleeunCursorForLoop) 2. Ningnestudiantepuedequedarinscritoenmsde3materias(sugerenciaemplee LOOPSanidados) 3. Ningnestudiantepuedaquedarinscritoenmenosde1materia(sugerencia empleeLOOPSanidados)28 6. Sehabrnrealizadotresevaluacionesparacadamateria,lasevaluacionesfueron realizadaslosdas:17deEnero,17deFebreroy17deMarzodelaoencurso. EscribaelcdigoSQL(Sugerenciaunbloqueiterativopodraayudarleaoptimizareste proceso) 7. EscribaunbloquedePL/SQLqueotorguenalosestudiantesinscritosencadamateria lascalificacionesobtenidasenlastresevaluacionesrealizadassugerenciaemplee ForCursorLoopsanidados,cumpliendoconlassiguientesreglas: 1. Lanotamnimaes1 2. Lanotamximaes5 3. Seempleancalificacionescon2decimales 8. EscribaunpaqueteenPL/SQLquecuenteconlassiguientesfuncionesyo procedimientos: 1. INFO_CURSO(VID_CURSOINNUMBER):recibecomoparmetroelIDdeun curso.Imprimeenpantallalasiguienteinformacin: 1. NombredelCurso 2. AlumnosInscritos 3. NotaMedia 4. NotaMnima 5. NotaMxima 2. INFO_ESTUDIANTE(VDOCUMENTO_ESTUDIANTEINVARCHAR2):recibecomo parmetroelDOCUMENTOdeunalumno.Imprimeenpantallalasiguiente informacin: 1. NombreyApellidodelEstudiante 2. Paracadaunadelasmateriasinscritas: 1. NombredelaMateria 2. Paracadaunodelosexmenespresentadosenesamateria: 1. Notaobtenidaenelexamen 3. NotaPromedioobtenidaenlamateria(definidacomolasumadelas calificacionesobtenidasenlosexmenesdivididoentreelnmerode exmenes) 3. NotaMximaobtenidaentodaslasmaterias(definidacomolacalificacinms altaobtenidaentodaslasevaluacionesDETODOSLOSCURSOS presentadasporestealumno)
28 Se sugiere realizar estos dos puntos en scripts separados al menos mientras se adquiere practica con los CURSORS y los LOOPS anidados
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 83 de 121

4. NotaMnimaobtenidaentodaslasmaterias(definidacomolacalificacinms bajaobtenidaentodaslasevaluacionesDETODOSLOSCURSOS presentadasporestealumno) SolucinPropuesta: Punto1y2:


--archivo para la creacion de tablas del modelo --requerido para la parte final del curso DROP DROP DROP DROP DROP TABLE TABLE TABLE TABLE TABLE ESTUDIANTE_EVALUACION; CURSO_ESTUDIANTE; EVALUACION; CURSO; ESTUDIANTE;

CREATE TABLE ESTUDIANTE( ID_ESTUDIANTE NUMBER, DOCUMENTO_ESTUDIANTE VARCHAR2(20) NOT NULL, NOMBRE_ESTUDIANTE VARCHAR2(50) NOT NULL, APELLIDO_ESTUDIANTE VARCHAR2(50) NOT NULL); ALTER TABLE ESTUDIANTE ADD CONSTRAINT EST_PKC PRIMARY KEY(ID_ESTUDIANTE); ALTER TABLE ESTUDIANTE ADD CONSTRAINT EST_UKC UNIQUE(DOCUMENTO_ESTUDIANTE); CREATE TABLE CURSO( ID_CURSO NUMBER, NOMBRE_CURSO VARCHAR2(50) NOT NULL); ALTER TABLE CURSO ADD CONSTRAINT CURSO_PKC PRIMARY KEY(ID_CURSO); ALTER TABLE CURSO ADD CONSTRAINT CURSO_UKC UNIQUE(NOMBRE_CURSO);

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 84 de 121

CREATE TABLE EVALUACION( ID_EVALUACION NUMBER, ID_CURSO NUMBER, FECHA_EVALUACION DATE NOT NULL); ALTER TABLE EVALUACION ADD CONSTRAINT EVAL_PKC PRIMARY KEY(ID_EVALUACION); ALTER TABLE EVALUACION ADD CONSTRAINT EVAL_FKC FOREIGN KEY(ID_CURSO) REFERENCES CURSO(ID_CURSO); CREATE TABLE CURSO_ESTUDIANTE( ID_CURSO NUMBER, ID_ESTUDIANTE NUMBER); ALTER TABLE CURSO_ESTUDIANTE ADD CONSTRAINT CEST_PKC PRIMARY KEY(ID_CURSO, ID_ESTUDIANTE)29; ALTER TABLE CURSO_ESTUDIANTE ADD CONSTRAINT CEST_FKC1 FOREIGN KEY30(ID_CURSO) REFERENCES CURSO(ID_CURSO); ALTER TABLE CURSO_ESTUDIANTE ADD CONSTRAINT CEST_FKC2 FOREIGN KEY31(ID_ESTUDIANTE) REFERENCES ESTUDIANTE(ID_ESTUDIANTE); CREATE TABLE ESTUDIANTE_EVALUACION( ID_ESTUDIANTE NUMBER, ID_EVALUACION NUMBER, CALIFICACION NUMBER NOT NULL); ALTER TABLE ESTUDIANTE_EVALUACION ADD CONSTRAINT EEVAL_PKC PRIMARY KEY(ID_ESTUDIANTE, ID_EVALUACION); ALTER TABLE ESTUDIANTE_EVALUACION
29 Llave primaria compuesta 30 Primera llave fornea de la tabla 31 Segunda llave fornea de la tabla
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 85 de 121

ADD CONSTRAINT EEVAL_FKC1 FOREIGN KEY(ID_ESTUDIANTE) REFERENCES ESTUDIANTE(ID_ESTUDIANTE); ALTER TABLE ESTUDIANTE_EVALUACION ADD CONSTRAINT EEVAL_FKC2 FOREIGN KEY(ID_EVALUACION) REFERENCES EVALUACION(ID_EVALUACION); --secuencias DROP SEQUENCE SEQ_ESTUDIANTE; DROP SEQUENCE SEQ_CURSO; DROP SEQUENCE SEQ_EVALUACION; CREATE SEQUENCE SEQ_ESTUDIANTE MINVALUE 1 START WITH 1 INCREMENT BY 1; CREATE SEQUENCE SEQ_CURSO MINVALUE 1 START WITH 1 INCREMENT BY 1; CREATE SEQUENCE SEQ_EVALUACION MINVALUE 1 START WITH 1 INCREMENT BY 1; .

Punto3y4:
-- insercion de los estudiantes INSERT INTO ESTUDIANTE VALUES(SEQ_ESTUDIANTE.NEXTVAL,'79899012','Juan','Perez'); INSERT INTO ESTUDIANTE VALUES(SEQ_ESTUDIANTE.NEXTVAL,'52780041','Rosa','Hernandez'); INSERT INTO ESTUDIANTE VALUES(SEQ_ESTUDIANTE.NEXTVAL,'53900789','Mara','Castro'); INSERT INTO ESTUDIANTE VALUES(SEQ_ESTUDIANTE.NEXTVAL,'80001000','Carlos','Hernandez'); INSERT INTO ESTUDIANTE VALUES(SEQ_ESTUDIANTE.NEXTVAL,'79998901','Pedro','Marin'); --insercion de las materias

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 86 de 121

INSERT INTO CURSO VALUES(SEQ_CURSO.NEXTVAL,'Matematicas'); INSERT INTO CURSO VALUES(SEQ_CURSO.NEXTVAL,'Geometria'); INSERT INTO CURSO VALUES(SEQ_CURSO.NEXTVAL,'Biologia'); INSERT INTO CURSO VALUES(SEQ_CURSO.NEXTVAL,'Bases de Datos'); INSERT INTO CURSO VALUES(SEQ_CURSO.NEXTVAL,'Programacion Orientada a Objetos'); --importante para no perder el trabajo en la proxima sesion COMMIT; .

Punto5:
--temporal para probar muchas veces /* DROP TABLE CURSO_ESTUDIANTE; CREATE TABLE CURSO_ESTUDIANTE( ID_CURSO NUMBER, ID_ESTUDIANTE NUMBER); ALTER TABLE CURSO_ESTUDIANTE ADD CONSTRAINT CEST_PKC PRIMARY KEY(ID_CURSO, ID_ESTUDIANTE); ALTER TABLE CURSO_ESTUDIANTE ADD CONSTRAINT CEST_FKC1 FOREIGN KEY(ID_CURSO) REFERENCES CURSO(ID_CURSO); ALTER TABLE CURSO_ESTUDIANTE ADD CONSTRAINT CEST_FKC2 FOREIGN KEY(ID_ESTUDIANTE) REFERENCES ESTUDIANTE(ID_ESTUDIANTE); */ --temporal para probar muchas veces DECLARE V_MAT NUMBER := 0; V_EST NUMBER := 0;

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 87 de 121

V_AUX NUMBER := 0; V_AUX2 NUMBER := 0; CURSOR C_MATERIA IS SELECT ID_CURSO FROM CURSO; BEGIN FOR R_MATERIA IN C_MATERIA LOOP --En cada materia deben quedar inscritos como minimo 2 estudiantes como maximo 4 FOR i IN 1..ROUND(DBMS_RANDOM.VALUE(2,4),0) LOOP LOOP V_AUX := ROUND(DBMS_RANDOM.VALUE(1,5),0); --un estudiante al azar SELECT COUNT(ID_ESTUDIANTE) INTO V_MAT FROM CURSO_ESTUDIANTE WHERE ID_ESTUDIANTE = V_AUX; --Ningun estudiante puede quedar inscrito en ms de 3 materias IF(V_MAT <= 3)THEN --pero tampoco puede estar inscrito dos veces en la misma materia SELECT COUNT(ID_ESTUDIANTE) INTO V_AUX2 FROM CURSO_ESTUDIANTE WHERE ID_CURSO = R_MATERIA.ID_CURSO AND ID_ESTUDIANTE = V_AUX; IF(V_AUX2 = 0)THEN INSERT INTO CURSO_ESTUDIANTE VALUES(R_MATERIA.ID_CURSO,V_AUX); EXIT;--Si se inserta el registro, se sale de este loop END IF; END IF; END LOOP; END LOOP; END LOOP; --importante para no perder el trabajo en la proxima sesion COMMIT; EXCEPTION WHEN OTHERS THEN RAISE_APPLICATION_ERROR(-20001,'An error was encountered - '|| SQLCODE||' -ERROR- '||SQLERRM); END;
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 88 de 121

. /

Punto6:
BEGIN FOR i IN 1..5 LOOP --loop para las materias INSERT INTO EVALUACION VALUES(SEQ_EVALUACION.NEXTVAL,i,to_date('17-01-09','DD-MM-YY')); INSERT INTO EVALUACION VALUES(SEQ_EVALUACION.NEXTVAL,i,to_date('17-02-09','DD-MM-YY')); INSERT INTO EVALUACION VALUES(SEQ_EVALUACION.NEXTVAL,i,to_date('17-03-09','DD-MM-YY')); END LOOP; --importante para no perder el trabajo en la proxima sesion COMMIT; EXCEPTION WHEN OTHERS THEN RAISE_APPLICATION_ERROR(-20001,'An error was encountered - '|| SQLCODE||' -ERROR- '||SQLERRM); END; . /

Punto7:
--Bloque PL/SQL que asigna las calificaciones para las evaluaciones existentes DECLARE CURSOR C_MATERIA IS --TODOS LOS CURSOS SELECT * FROM CURSO; CURSOR C_ESTUDIANTE(VIN_IDCURSO IN NUMBER) IS --TODOS LOS ESTUDIANTES EN UN CURSO SELECT * FROM CURSO_ESTUDIANTE WHERE ID_CURSO = VIN_IDCURSO;

CURSOR C_EVALUACION(VIN_IDCURSO IN NUMBER) IS --TODAS LAS EVALUACIONES PARA UN CURSO


Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 89 de 121

SELECT * FROM EVALUACION WHERE ID_CURSO = VIN_IDCURSO; BEGIN FOR R_MATERIA IN C_MATERIA LOOP FOR R_ESTUDIANTE IN C_ESTUDIANTE(R_MATERIA.ID_CURSO) LOOP FOR R_EVALUACION IN C_EVALUACION(R_MATERIA.ID_CURSO) LOOP INSERT INTO ESTUDIANTE_EVALUACION VALUES(R_ESTUDIANTE.ID_ESTUDIANTE,R_EVALUACION.ID_EVALUACION,ROUND( DBMS_RANDOM.VALUE(1,5),2)); END LOOP; END LOOP; END LOOP; --importante para no perder el trabajo en la proxima sesion COMMIT; EXCEPTION WHEN OTHERS THEN RAISE_APPLICATION_ERROR(-20001,'An error was encountered - '|| SQLCODE||' -ERROR- '||SQLERRM); END; . /

Punto8: Spec.delPaquete:
CREATE OR REPLACE PACKAGE COURSES AS FUNCTION NOTA_MEDIA_CURSO(VID_CURSO IN CURSO.ID_CURSO%TYPE) RETURN NUMBER; FUNCTION NOTA_MINIMA_CURSO(VID_CURSO IN CURSO.ID_CURSO%TYPE) RETURN NUMBER; FUNCTION NOTA_MAXIMA_CURSO(VID_CURSO IN CURSO.ID_CURSO%TYPE) RETURN NUMBER; FUNCTION NOTA_MEDIA_ESTUDIANTE(VID_ESTUDIANTE IN ESTUDIANTE.ID_ESTUDIANTE %TYPE, VID_CURSO IN CURSO.ID_CURSO%TYPE) RETURN NUMBER; FUNCTION NOTA_MINIMA_ESTUDIANTE(VID_ESTUDIANTE IN
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 90 de 121

ESTUDIANTE.ID_ESTUDIANTE%TYPE) RETURN NUMBER; FUNCTION NOTA_MAXIMA_ESTUDIANTE(VID_ESTUDIANTE IN ESTUDIANTE.ID_ESTUDIANTE%TYPE) RETURN NUMBER; PROCEDURE INFO_CURSO(VID_CURSO IN CURSO.ID_CURSO%TYPE); PROCEDURE INFO_ESTUDIANTE(VDOCUMENTO_ESTUDIANTE IN ESTUDIANTE.DOCUMENTO_ESTUDIANTE%TYPE); /* PROCEDURE HELP32; PROCEDURE TEST33; */ END COURSES; . /

BodydelPaquete:
CREATE OR REPLACE PACKAGE BODY COURSES AS --DADO UN ID_CURSO RECUPERAR TODAS LAS EVALUACIONES DE ESE CURSO CURSOR C_EVALUACIONES(CVID_CURSO IN CURSO.ID_CURSO%TYPE) IS SELECT ID_EVALUACION FROM EVALUACION WHERE ID_CURSO = CVID_CURSO; --DADO UN ID_ESTUDIANTE RECUPERAR EL VALOR MAXIMO --DENTRO DE TODAS LAS EVALUACIONES DE ESE ESTUDIANTE CURSOR C_EVALUACIONESEST1(CVID_ESTUDIANTE IN ESTUDIANTE.ID_ESTUDIANTE %TYPE) IS SELECT MAX(CALIFICACION) AS NOTAMAXIMA FROM ESTUDIANTE_EVALUACION WHERE ID_ESTUDIANTE = CVID_ESTUDIANTE; R_EVALUACIONESEST1 C_EVALUACIONESEST1%ROWTYPE;
32 Aunque no estn definidos en este ejemplo es una prctica recomendable incluir en sus paquetes un procedimiento que sirva como ayuda para quien desea usarlo. 33 Resulta muy recomendable tambin incluir un procedimiento que cuente con las diferentes unidades de prueba para cada funcin y procedimiento definido en el paquete.
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 91 de 121

--DADO UN ID_ESTUDIANTE RECUPERAR EL VALOR MAXIMO --DENTRO DE TODAS LAS EVALUACIONES DE ESE ESTUDIANTE CURSOR C_EVALUACIONESEST2(CVID_ESTUDIANTE IN ESTUDIANTE.ID_ESTUDIANTE %TYPE) IS SELECT MIN(CALIFICACION) AS NOTAMINIMA FROM ESTUDIANTE_EVALUACION WHERE ID_ESTUDIANTE = CVID_ESTUDIANTE; R_EVALUACIONESEST2 C_EVALUACIONESEST2%ROWTYPE; --DADO UN ID_EVALUACION RECUPERAR LA SUMA DE --LAS CALIFICACIONES PARA ESA EVALUACION CURSOR CC_CALIFICACIONES1(CVID_EVALUACION IN ESTUDIANTE_EVALUACION.ID_EVALUACION%TYPE) IS SELECT SUM(CALIFICACION) AS TOTALSUMA FROM ESTUDIANTE_EVALUACION WHERE ID_EVALUACION = CVID_EVALUACION; R_CALIFICACIONES1 CC_CALIFICACIONES1%ROWTYPE; --DADO UN ID_EVALUACION RECUPERAR EL NUMERO DE --CALIFICACIONES PARA ESA EVALUACION CURSOR CC_CALIFICACIONES2(CVID_EVALUACION IN ESTUDIANTE_EVALUACION.ID_EVALUACION%TYPE) IS SELECT COUNT(CALIFICACION) AS NUMEVAL FROM ESTUDIANTE_EVALUACION WHERE ID_EVALUACION = CVID_EVALUACION; R_CALIFICACIONES2 CC_CALIFICACIONES2%ROWTYPE; --DADO UN ID_EVALUACION RECUPERAR EL VALOR MINIMO DE --LAS CALIFICACIONES PARA ESA EVALUACION CURSOR CC_CALIFICACIONES3(CVID_EVALUACION IN ESTUDIANTE_EVALUACION.ID_EVALUACION%TYPE) IS SELECT MIN(CALIFICACION) AS NOTAMINIMA FROM ESTUDIANTE_EVALUACION WHERE ID_EVALUACION = CVID_EVALUACION; R_CALIFICACIONES3 CC_CALIFICACIONES3%ROWTYPE; --DADO UN ID_EVALUACION RECUPERAR EL VALOR MAXIMO DE --LAS CALIFICACIONES PARA ESA EVALUACION CURSOR CC_CALIFICACIONES4(CVID_EVALUACION IN ESTUDIANTE_EVALUACION.ID_EVALUACION%TYPE) IS
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 92 de 121

SELECT MAX(CALIFICACION) AS NOTAMAXIMA FROM ESTUDIANTE_EVALUACION WHERE ID_EVALUACION = CVID_EVALUACION; R_CALIFICACIONES4 CC_CALIFICACIONES4%ROWTYPE; --DADO EL ID_CURSO RECUPERAR LOS ESTUDIANTES INSCRITOS CURSOR C_ESTUDIANTES_INSCRITOS(CVID_CURSO IN CURSO.ID_CURSO%TYPE) IS SELECT * FROM ESTUDIANTE E JOIN CURSO_ESTUDIANTE CE ON E.ID_ESTUDIANTE = CE.ID_ESTUDIANTE WHERE CE.ID_CURSO = CVID_CURSO; --DADO EL ID_ESTUDIANTE RECUPERAR LOS CURSOS EN LOS --QUE SE ENCUENTRA INSCRITO CURSOR C_INSCRITOEN(CVID_ESTUDIANTE IN ESTUDIANTE.ID_ESTUDIANTE%TYPE) IS SELECT ID_CURSO FROM CURSO_ESTUDIANTE WHERE ID_ESTUDIANTE = CVID_ESTUDIANTE;

FUNCTION NOTA_MEDIA_CURSO(VID_CURSO IN CURSO.ID_CURSO%TYPE) RETURN NUMBER IS V_AUX_COUNTER NUMBER := 0; V_AUX_SUM NUMBER := 0; BEGIN FOR R_EVALUACIONES IN C_EVALUACIONES(VID_CURSO) LOOP V_AUX_COUNTER := V_AUX_COUNTER + 1; OPEN CC_CALIFICACIONES1(R_EVALUACIONES.ID_EVALUACION); FETCH CC_CALIFICACIONES1 INTO R_CALIFICACIONES1; CLOSE CC_CALIFICACIONES1; OPEN CC_CALIFICACIONES2(R_EVALUACIONES.ID_EVALUACION); FETCH CC_CALIFICACIONES2 INTO R_CALIFICACIONES2; CLOSE CC_CALIFICACIONES2; V_AUX_SUM := V_AUX_SUM+ (R_CALIFICACIONES1.TOTALSUMA/R_CALIFICACIONES2.NUMEVAL); END LOOP; RETURN ROUND((V_AUX_SUM/V_AUX_COUNTER),2);

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 93 de 121

END NOTA_MEDIA_CURSO;

FUNCTION NOTA_MINIMA_CURSO(VID_CURSO IN CURSO.ID_CURSO%TYPE) RETURN NUMBER IS V_NOTA_ACTUAL NUMBER := 0; V_NOTA_MINIMA NUMBER := 5; --SE BUSCA QUE LA PRIMERA NOTA OBTENIDA SEA MENOR BEGIN FOR R_EVALUACIONES IN C_EVALUACIONES(VID_CURSO) LOOP OPEN CC_CALIFICACIONES3(R_EVALUACIONES.ID_EVALUACION); FETCH CC_CALIFICACIONES3 INTO R_CALIFICACIONES3; CLOSE CC_CALIFICACIONES3; V_NOTA_ACTUAL := R_CALIFICACIONES3.NOTAMINIMA; IF(V_NOTA_ACTUAL < V_NOTA_MINIMA) THEN V_NOTA_MINIMA := V_NOTA_ACTUAL;--EN CASO DE QUE LA NOTA ACTUAL SEA MAYOR QUE LA MINIMA OBTENIDA NO HAY CAMBIO END IF; END LOOP; RETURN V_NOTA_MINIMA; END NOTA_MINIMA_CURSO;

FUNCTION NOTA_MAXIMA_CURSO(VID_CURSO IN CURSO.ID_CURSO%TYPE) RETURN NUMBER IS V_NOTA_ACTUAL NUMBER := 0; V_NOTA_MAXIMA NUMBER := 0; --SE BUSCA QUE LA PRIMERA NOTA OBTENIDA SEA MAYOR BEGIN FOR R_EVALUACIONES IN C_EVALUACIONES(VID_CURSO) LOOP OPEN CC_CALIFICACIONES4(R_EVALUACIONES.ID_EVALUACION); FETCH CC_CALIFICACIONES4 INTO R_CALIFICACIONES4; CLOSE CC_CALIFICACIONES4; V_NOTA_ACTUAL := R_CALIFICACIONES4.NOTAMAXIMA; IF(V_NOTA_ACTUAL > V_NOTA_MAXIMA) THEN V_NOTA_MAXIMA := V_NOTA_ACTUAL;--EN CASO DE QUE LA NOTA ACTUAL SEA MAYOR QUE LA MINIMA OBTENIDA NO HAY CAMBIO END IF;
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 94 de 121

END LOOP; RETURN V_NOTA_MAXIMA; END NOTA_MAXIMA_CURSO; FUNCTION NOTA_MEDIA_ESTUDIANTE(VID_ESTUDIANTE IN ESTUDIANTE.ID_ESTUDIANTE %TYPE, VID_CURSO IN CURSO.ID_CURSO%TYPE) RETURN NUMBER IS V_AUX_SUMA NUMBER := 0; V_AUX NUMBER := 0; V_AUX_CONT NUMBER := 0; BEGIN --RECUPERAR EVALUACIONES PARA ESTE CURSO FOR R_EVALUACIONES IN C_EVALUACIONES(VID_CURSO) LOOP V_AUX_CONT := V_AUX_CONT+1; SELECT CALIFICACION INTO V_AUX FROM ESTUDIANTE_EVALUACION WHERE ID_EVALUACION = R_EVALUACIONES.ID_EVALUACION AND ID_ESTUDIANTE = VID_ESTUDIANTE; V_AUX_SUMA := V_AUX_SUMA + V_AUX; END LOOP; RETURN ROUND((V_AUX_SUMA/V_AUX_CONT),2); END NOTA_MEDIA_ESTUDIANTE; FUNCTION NOTA_MINIMA_ESTUDIANTE(VID_ESTUDIANTE IN ESTUDIANTE.ID_ESTUDIANTE%TYPE) RETURN NUMBER IS BEGIN OPEN C_EVALUACIONESEST2(VID_ESTUDIANTE); FETCH C_EVALUACIONESEST2 INTO R_EVALUACIONESEST2; CLOSE C_EVALUACIONESEST2; RETURN R_EVALUACIONESEST2.NOTAMINIMA; END NOTA_MINIMA_ESTUDIANTE;

FUNCTION NOTA_MAXIMA_ESTUDIANTE(VID_ESTUDIANTE IN ESTUDIANTE.ID_ESTUDIANTE%TYPE) RETURN NUMBER IS BEGIN OPEN C_EVALUACIONESEST1(VID_ESTUDIANTE);


Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 95 de 121

FETCH C_EVALUACIONESEST1 INTO R_EVALUACIONESEST1; CLOSE C_EVALUACIONESEST1; RETURN R_EVALUACIONESEST1.NOTAMAXIMA; END NOTA_MAXIMA_ESTUDIANTE;

PROCEDURE INFO_CURSO(VID_CURSO IN CURSO.ID_CURSO%TYPE) IS V_AUXNOMBRE CURSO.NOMBRE_CURSO%TYPE; BEGIN SELECT NOMBRE_CURSO INTO V_AUXNOMBRE FROM CURSO WHERE ID_CURSO = VID_CURSO; PL('--- INFORMACION SOLICITADA DEL CURSO ---'); PL('Nombre del Curso: '||V_AUXNOMBRE); PL('Alumnos Inscritos: '); FOR R_ESTUDIANTES_INSCRITOS IN C_ESTUDIANTES_INSCRITOS(VID_CURSO) LOOP PL(R_ESTUDIANTES_INSCRITOS.NOMBRE_ESTUDIANTE||' '|| R_ESTUDIANTES_INSCRITOS.APELLIDO_ESTUDIANTE||' '|| R_ESTUDIANTES_INSCRITOS.DOCUMENTO_ESTUDIANTE); END LOOP; PL('Nota Media Obtenida por los Estudiantes: '|| to_char(NOTA_MEDIA_CURSO(VID_CURSO))); PL('Nota Minima Obtenida por los Estudiantes: '|| to_char(NOTA_MINIMA_CURSO(VID_CURSO))); PL('Nota Maxima Obtenida por los Estudiantes: '|| to_char(NOTA_MAXIMA_CURSO(VID_CURSO))); END INFO_CURSO;

PROCEDURE INFO_ESTUDIANTE(VDOCUMENTO_ESTUDIANTE IN ESTUDIANTE.DOCUMENTO_ESTUDIANTE%TYPE) IS R_ESTUDIANTE ESTUDIANTE%ROWTYPE; R_CURSO CURSO%ROWTYPE; R_ESTUDIANTE_EVALUACION ESTUDIANTE_EVALUACION%ROWTYPE; BEGIN SELECT * INTO R_ESTUDIANTE FROM ESTUDIANTE WHERE DOCUMENTO_ESTUDIANTE LIKE VDOCUMENTO_ESTUDIANTE; PL('--- INFORMACION SOLICITADA DEL ESTUDIANTE ---'); PL('Nombre: '||R_ESTUDIANTE.NOMBRE_ESTUDIANTE||' Apellido: '|| R_ESTUDIANTE.APELLIDO_ESTUDIANTE||' Documento: '||
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 96 de 121

R_ESTUDIANTE.DOCUMENTO_ESTUDIANTE); FOR R_INSCRITOEN IN C_INSCRITOEN(R_ESTUDIANTE.ID_ESTUDIANTE) LOOP SELECT * INTO R_CURSO FROM CURSO WHERE ID_CURSO = R_INSCRITOEN.ID_CURSO; PL('Inscrito en: '||R_CURSO.NOMBRE_CURSO); FOR R_EVALUACIONES IN C_EVALUACIONES(R_INSCRITOEN.ID_CURSO) LOOP SELECT * INTO R_ESTUDIANTE_EVALUACION FROM ESTUDIANTE_EVALUACION WHERE ID_ESTUDIANTE = R_ESTUDIANTE.ID_ESTUDIANTE AND ID_EVALUACION = R_EVALUACIONES.ID_EVALUACION; PL('Evaluacion: '||R_EVALUACIONES.ID_EVALUACION||' Calificacion Obtenida: '||R_ESTUDIANTE_EVALUACION.CALIFICACION); END LOOP; PL('Nota Media Obtenida: '|| to_char(NOTA_MEDIA_ESTUDIANTE(R_ESTUDIANTE.ID_ESTUDIANTE,R_INSCRITOEN.ID_CU RSO))); END LOOP; PL('Nota Maxima General Obtenida: '|| to_char(NOTA_MAXIMA_ESTUDIANTE(R_ESTUDIANTE.ID_ESTUDIANTE))); PL('Nota Minima General Obtenida: '|| to_char(NOTA_MINIMA_ESTUDIANTE(R_ESTUDIANTE.ID_ESTUDIANTE))); EXCEPTION WHEN NO_DATA_FOUND THEN PL('NO EXISTE UN ALUMNO CON ESE DOCUMENTO'); END INFO_ESTUDIANTE; /* PROCEDURE HELP IS BEGIN END HELP;

PROCEDURE TEST IS BEGIN END TEST; */ END COURSES; . /


Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 97 de 121

7.6ForUpdate: EnunaoperacinSELECTnosebloquean(lock)lasfilasseleccionadaspuessetratade unaoperacindeconsultaestoquieredecirquemientrasunusuarioestleyendola informacinalmacenadaenunaovariastablas,otrousuariopuedeestarmodificandola misma.Paraelejemplodesarrolladoanteriormenteunusuariopodraestarleyendoel informedeunalumnoodeuncurso,mientrasqueotrousuarioestactualizandoestamisma informacin,deformaquecuandoelprimerosolicitedenuevolainformacinverlos cambiossuministradosporelsegundo. HastaelmomentolosCursoresexpuestoshantrabajadoconconsultas,peroquocurresi sedeseaintroducirunafuncinqueactualicetodoslosregistrosrecuperadosporuncursor? enesecasosehaceindispensablequelainformacinrecuperadaquedebloqueada(lock) hastaquelaoperacindeactualizacintermine,evitandoasqueunsegundousuariorealice modificacionesindeseadas. Ejemplo1:EscribaunprocedimientodePL/SQLeintgreloalpaqueteCOURSES desarrolladoanteriormente,queactualicelascalificacionesdelosestudiantesinscritosenun cursoincrementndolasen1siempreycuandosuvalorluegodeserincrementadasno seamayora5. SolucinPropuesta: ArchivoSpec:
PROCEDURE SUBIR_CALIFICACIONES(VID_CURSO IN CURSO.ID_CURSO%TYPE);

ArchivoBody:
PROCEDURE SUBIR_CALIFICACIONES(VID_CURSO IN CURSO.ID_CURSO%TYPE) --observe la definicion del cursor que se emplea en este procedimiento IS CURSOR C_CALIFICACIONES_FU(CVID_EVALUACION IN ESTUDIANTE_EVALUACION.ID_EVALUACION%TYPE) IS--variable local SELECT * FROM ESTUDIANTE_EVALUACION WHERE ID_EVALUACION = CVID_EVALUACION FOR UPDATE; --al incluir For Update en el Cursor se establece un bloqueo sobre los registros recuperados BEGIN FOR R_EVALUACIONES IN C_EVALUACIONES(VID_CURSO) LOOP --C_EVALUACIONES ES VARIABLE GLOBAL
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 98 de 121

FOR R_CALIFICACIONES_FU IN C_CALIFICACIONES_FU(R_EVALUACIONES.ID_EVALUACION) LOOP IF R_CALIFICACIONES_FU.CALIFICACION <= 4 THEN UPDATE ESTUDIANTE_EVALUACION SET CALIFICACION = R_CALIFICACIONES_FU.CALIFICACION + 1 WHERE ID_EVALUACION = R_CALIFICACIONES_FU.ID_EVALUACION AND ID_ESTUDIANTE = R_CALIFICACIONES_FU.ID_ESTUDIANTE; END IF; END LOOP; END LOOP; COMMIT; -- Luego del commit el bloqueo sobre los registros termina y otros usuarios/programas pueden modificarlos EXCEPTION WHEN NO_DATA_FOUND THEN PL('NO EXISTE UN CURSO CON ESE CODIGO'); END;

TestUnit(unaimagenvalemasquemilpalabras):

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 99 de 121

Elmismoprocedimientopuedeescribirsedelasiguientemanera: Spec:
PROCEDURE SUBIR_CALIFICACIONES2(VID_CURSO IN CURSO.ID_CURSO%TYPE);

Body:
PROCEDURE SUBIR_CALIFICACIONES2(VID_CURSO IN CURSO.ID_CURSO%TYPE) --observe la definicion del cursor que se emplea en este procedimiento IS CURSOR C_CALIFICACIONES_FU(CVID_EVALUACION IN ESTUDIANTE_EVALUACION.ID_EVALUACION%TYPE) IS--variable local SELECT * FROM ESTUDIANTE_EVALUACION WHERE ID_EVALUACION = CVID_EVALUACION FOR UPDATE OF CALIFICACION; --al incluir For Update Of se bloquea solo el campo especificado BEGIN FOR R_EVALUACIONES IN C_EVALUACIONES(VID_CURSO) LOOP --C_EVALUACIONES ES VARIABLE GLOBAL FOR R_CALIFICACIONES_FU IN C_CALIFICACIONES_FU(R_EVALUACIONES.ID_EVALUACION) LOOP IF R_CALIFICACIONES_FU.CALIFICACION <= 4 THEN UPDATE ESTUDIANTE_EVALUACION SET CALIFICACION = R_CALIFICACIONES_FU.CALIFICACION + 1 WHERE CURRENT OF C_CALIFICACIONES_FU; -- Where Current Of solo puede usarse con For Update Of END IF; END LOOP; END LOOP;

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 100 de 121

COMMIT; -- Luego del commit el bloqueo sobre los registros termina y otros usuarios/programas pueden modificarlos EXCEPTION WHEN NO_DATA_FOUND THEN PL('NO EXISTE UN CURSO CON ESE CODIGO'); END;

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 101 de 121

8.DevueltaalosTriggers: HacealgntiemposehabldeTriggersenestedocumento,ahoraconlasbasesadquiridas puederetomarseeltemayahondarseenelmismo.Recuerdequeuntriggeresunbloquede cdigoPL/SQLqueseencuentraalmacenadoenlabasededatosyqueseejecutadeforma automticacuandoocurrenlossiguienteseventos: 1. EventosrelacionadosconDML34:INSERT,UPDATE,DELETE.LosTriggersdefinidos paraestoseventospuedenocurrirantes(before)odespus(after)dequeocurran. 2. EventosrelacionadosconDDL35:CREATEoALTER.EstetipodeTriggersseemplea enauditorasdeseguridadygeneralmenteserelacionanconusuariosoesquemas36 particularesdelabasededatos. 3. UneventodelsistemacomoelEncendidooApagadodelaBasedeDatos. 4. Uneventodeusuariocomoelaccesooladesconexin. LasintaxisgeneralparadefinirunTriggereslasiguiente:
CREATE [OR REPLACE] TRIGGER Trigger_name {BEFORE|AFTER} Triggering_event ON table_name [FOR EACH ROW] [WHEN condition] DECLARE declaration statements BEGIN executable statements EXCEPTION exception-handling statements END;

ParahabilitarunTriggerseemplealasiguienteinstruccin:
ALTER TRIGGER trigger_name ENABLE;

DemanerasimilarparadeshabilitarunTriggerseemplealasiguienteinstruccin:
ALTER TRIGGER trigger_name DISABLE;

34 Data Manipulation Language 35 Data Definition Language 36 A schema is a collection of database objects. A schema is owned by a database user and has the same name as that user.
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 102 de 121

Sisedeseaasociarmsdeuntriggeralamismatablaesposibleemplearlaclusula FOLLOWSparaindicarelordenenquedebenejecutarselosTriggers37:
ALTER TRIGGER trigger_name FOLLOWS trigger_name238;

Ejemplo1:Definauntriggerqueimpidalacreacindeunestudianteconelnombresystem39 SolucinPropuesta:
CREATE OR REPLACE TRIGGER AVOID_RESERVED_NAMES BEFORE INSERT ON ESTUDIANTE FOR EACH ROW DECLARE MSG VARCHAR2(50) := 'No esta permitido crear estudiantes con ese nombre'; BEGIN IF(UPPER(:NEW.NOMBRE_ESTUDIANTE) = 'SYSTEM') THEN RAISE_APPLICATION_ERROR(-20001,MSG); END IF; --EXCEPTION END AVOID_RESERVED_NAMES; . /

Cuandoseintentainsertarunregistroconesenombredeusuarioelsistemamuestraunerror comoelsiguiente:
INSERT INTO ESTUDIANTE VALUES(SEQ_ESTUDIANTE.NEXTVAL,'999999','SYSTEM','123'); VALUES(SEQ_ESTUDIANTE.NEXTVAL,'999999','SYSTEM','123') * ERROR at line 2: ORA-20001: No esta permitido crear estudiantes con ese nombre ORA-06512: at "SYSTEM.AVOID_RESERVED_NAMES", line 5 ORA-04088: error during execution of trigger 'SYSTEM.AVOID_RESERVED_NAMES'

AhoraquesehacubiertoeltemadeRecordspuedeexplicarseque:NEWesenrealidaduna especiedeRecord(pseudorecord)delmismotipodelatablaalaqueseencuentraasociado elTrigger.


37 De otra manera Oracle puede disparar los Triggers en cualquier orden. 38 Se asume que los dos Triggers han sido definidos para trabajar sobre la misma tabla. 39 Aunque es un ejemplo en la prctica se definen restricciones sobre los nombres de usuario que pueden almacenarse.
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 103 de 121

Ejemplo2:definaunTriggerqueimpidalaactualizacindeunacalificacinempleandoun valormenoralexistente. SolucinPropuesta:


CREATE OR REPLACE TRIGGER AVOID_INCORRECT_GRADES_UPDATES BEFORE UPDATE ON ESTUDIANTE_EVALUACION FOR EACH ROW DECLARE MSG VARCHAR2(100) := 'No es posible modificar la calificacion por una de menor valor'; BEGIN IF(:NEW.CALIFICACION < :OLD.CALIFICACION) THEN RAISE_APPLICATION_ERROR(-20001,MSG); END IF; --EXCEPTION END AVOID_INCORRECT_GRADES_UPDATES; . /

Observeelusode:OLDenelejemploanterior,aligualque:NEWesunpseudorecorddel mismotipoquelatablaalaqueseestasociandoelTrigger.Esimportantetenerencuenta que:OLDcorrespondealainformacinqueactualmentetieneelregistro,mientrasque:NEW correspondealainformacinqueestintentandoingresarsealregistro. Ejemplo3:ConlaayudadeunanuevatabladenominadaESTADISTICAconlossiguientes campos:ID_ESTADISTICA(NUMBER),TIPO_TRANSACCION(VARCHAR220), USUARIO(VARCHAR220),FECHA(DATE).EscribaunTriggerquelleveunregistrodelas operacionesrealizadassobrelatablaESTUDIANTE_EVALUACION,detalmaneraqueen cualquiermomentoseaposibledeterminarquienmodificlosregistrosdeestatabla (actualizndolosoeliminndolos)yenquefechalohizo. SolucinPropuesta:
DROP TABLE ESTADISTICA; CREATE TABLE ESTADISTICA (ID_ESTADISTICA NUMBER, TIPO_TRANSACCION VARCHAR2(20), USUARIO VARCHAR2(20), FECHA DATE);

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 104 de 121

DROP SEQUENCE SEQ_ESTADISTICA; CREATE SEQUENCE SEQ_ESTADISTICA MINVALUE 1 START WITH 1 INCREMENT BY 1 NOCACHE; CREATE OR REPLACE TRIGGER AUDIT_ESTUDIANTE_EVALUACION AFTER UPDATE OR DELETE ON ESTUDIANTE_EVALUACION FOR EACH ROW DECLARE V_TIPO VARCHAR2(20); BEGIN IF UPDATING THEN V_TIPO := 'UPDATE'; ELSIF DELETING THEN V_TIPO := 'DELETE'; END IF; INSERT INTO ESTADISTICA VALUES(SEQ_ESTADISTICA.NEXTVAL, V_TIPO, USER, SYSDATE); END AUDIT_ESTUDIANTE_EVALUACION; . /

UnidaddePrueba: Serealizalasiguienteactualizacinenelsistema:
UPDATE ESTUDIANTE_EVALUACION SET CALIFICACION=CALIFICACION+0.5 WHERE ID_ESTUDIANTE=3 AND ID_EVALUACION=15;

AhoraalconsultarlatablaESTADISTICAseobtienelainformacinrelacionadaconesta operacin.

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 105 de 121

Ejercicio1:definaunTriggerqueguardeenlatablaESTADISTICApreviamentecreadala informacinrelacionadaconlasoperaciones:UPDATE,DELETEqueserealizansobrela tablaESTUDIANTE.ModifiquelatablaESTADISTICAparaquealmaceneelnombredela tablasobrelaqueelusuariorealizlaoperacin. SolucinPropuesta:


TRUNCATE TABLE ESTADISTICA; ALTER TABLE ESTADISTICA ADD (NOMBRE_TABLA VARCHAR2(30)); CREATE OR REPLACE TRIGGER AUDIT_ESTUDIANTE AFTER UPDATE OR DELETE ON ESTUDIANTE FOR EACH ROW DECLARE V_TIPO VARCHAR2(20); V_TABLA VARCHAR2(30); BEGIN IF UPDATING THEN V_TIPO := 'UPDATE'; V_TABLA := 'ESTUDIANTE'; ELSIF DELETING THEN V_TIPO := 'DELETE'; V_TABLA := 'ESTUDIANTE'; END IF; INSERT INTO ESTADISTICA VALUES(SEQ_ESTADISTICA.NEXTVAL, V_TIPO, USER, SYSDATE, V_TABLA); END AUDIT_ESTUDIANTE; . /

Ejercicio2:definaunTriggerqueguardeenlatablaESTADISTICApreviamentemodificada lainformacinrelacionadaconlaoperacinINSERTqueserealizasobrelatabla ESTUDIANTE.EsteTriggerdebedispararseDESPUESdequelaoperacindeINSERCION serealiza.

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 106 de 121

SolucinPropuesta:
CREATE OR REPLACE TRIGGER AUDIT_ESTUDIANTE2 AFTER INSERT ON ESTUDIANTE FOR EACH ROW DECLARE V_TIPO VARCHAR2(20); V_TABLA VARCHAR2(30); BEGIN V_TIPO := 'INSERT'; V_TABLA := 'ESTUDIANTE'; INSERT INTO ESTADISTICA VALUES(SEQ_ESTADISTICA.NEXTVAL, V_TIPO, USER, SYSDATE, V_TABLA); END AUDIT_ESTUDIANTE2; . /

8.1PRAGMAAUTONOMOUS_TRANSACTION: EsposiblellevarlosTriggersunpasomsadelanteincluyendoenelloslasdenominadas TransaccionesAutnomas,quenosonotracosaquetransaccionesqueseefectancomo respuestaaotrastransacciones.Considreseelejemploanterior,quocurrirasila operacindeinsercinfalla?seperderaelregistrodeauditora? Lassiguientesimgenesilustranmejorloquesequieredecir:

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 107 de 121

DeestamaneraelTriggerqueactualmenteestdefinidonollevaraunregistrodelos intentosfallidosdeinsercinderegistros;algoqueenmuchoscasosessumamente importantedependiendodeltipodeauditoraqueseestrealizando.Puedemodificarseel TriggeranteriorparaqueindependientementedelresultadodelaoperacindeInsercinlleve unregistrodelamisma.


CREATE OR REPLACE TRIGGER AUDIT_ESTUDIANTE2 BEFORE40 INSERT ON ESTUDIANTE FOR EACH ROW DECLARE V_TIPO VARCHAR2(20); V_TABLA VARCHAR2(30); PRAGMA AUTONOMOUS_TRANSACTION;41 BEGIN V_TIPO := 'INSERT'; V_TABLA := 'ESTUDIANTE';
40 Es importante modificar el Trigger para que se active antes de la insercin, de lo contrario el hecho de que la operacin no se pueda realizar har que el Trigger no se dispare 41 Sin esta declaracin el Trigger no grabar nada en la tabla ESTADISTICA as se haya modificado el momento de ejecucin del mismo
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 108 de 121

INSERT INTO ESTADISTICA VALUES(SEQ_ESTADISTICA.NEXTVAL, V_TIPO, USER, SYSDATE, V_TABLA); COMMIT; END AUDIT_ESTUDIANTE2; . /

Ahoralaejecucindeunaoperacincomo:INSERT INTO ESTUDIANTE VALUES(SEQ_ESTUDIANTE.NEXTVAL,'98009','SYSTEM','123');generaunnuevoregistro enlatablaESTADISTICAsinimportarquelaoperacindeInsercinensifalledebidoal TriggerqueimpidelainsercinderegistrosconelnombredeusuarioSYSTEM. Ejercicio1:modifiquelatablaESTADISTICAparaquealmacenejuntoconlafechalahora enlaqueseprodujolamodificacinoelintentodesobrelastablasenlascualesseest realizandolaauditora. SolucinPropuesta:
TRUNCATE TABLE ESTADISTICA; ALTER TABLE ESTADISTICA MODIFY (FECHA TIMESTAMP); /

8.2INSTEADOFTrigger: OtraclasedeTriggerssonlosqueenlugardereaccionarodispararseantesodespus deuneventoespecfico,sustituyenaleventoens.EstaclasedeTriggersseemplean usualmenteenlasVistas. Ejemplo1:DefinaunavistaparalatablaEstudianteenrealidadnoexistirdiferenciaentre latablaylavista42yluegocreeunINSTEADOFTriggerquesustituyalaoperacinde INSERTparalavistaESTUDIDANTE_VIEWyseencarguedealmacenarenMaysculasel nombreyelapellidodelestudianteenelnuevoregistro.

42 Se crea la vista en este ejemplo para poder presentar el concepto de los Instead Of Triggers ya que los mismos no pueden crearse asociados a las Tablas
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 109 de 121

SolucinPropuesta:
CREATE OR REPLACE VIEW ESTUDIANTE_VIEW AS SELECT * FROM ESTUDIANTE; CREATE OR REPLACE TRIGGER TRG_INSERT_ESTUDIANTE INSTEAD OF INSERT ON ESTUDIANTE_VIEW FOR EACH ROW BEGIN INSERT INTO ESTUDIANTE VALUES (SEQ_ESTUDIANTE.NEXTVAL,:NEW.DOCUMENTO_ESTUDIANTE,UPPER(:NEW.NOMBRE_ESTUD IANTE),UPPER(:NEW.APELLIDO_ESTUDIANTE)); END TRG_INSERT_ESTUDIANTE; . /

Inserteunnuevoregistroenlatabla:
INSERT INTO ESTUDIANTE_VIEW VALUES(1,'88998899','Julio','peres');

Consultelatabla.AhoraentiendeelfuncionamientodeunTriggerdeltipoInsteadOf.

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 110 de 121

9.Colecciones: Alcomienzodeestecursosetrabajabarecuperandounregistroalavez,luegograciasala introduccindelosCursoresylosRecordsfueposibletrabajarsobregruposderegistros recuperandounregistroalavez,lascoleccionesamplanlasposibilidadesdelprogramador dePL/SQLpermitindolerecuperarmltiplesfilasalavezyalmacenndolasenuna coleccindemanerasimilaracomoloharaenunArrayencualquierlenguajede programacinmoderno. 9.1TablasPL/SQL: ElprimertipodecoleccinquesecubrirsedenominaTablaPL/SQL.UnatablaPL/SQLes similaraunatablaenlabasededatosOracleperoconunasolacolumna.Existendostipos deTablasPL/SQL:Asociativas(tambindenominadasIndexByTables)ylasTablas Anidadas(Nested). 9.1.1TablasIndexBy:unaTablaAsociativa(IndexBy)sedefinedelasiguientemanera:
TYPE type_name IS TABLE OF element_type [NOT NULL] INDEX BY element_type; table_name TYPE_NAME;

Ejemplo1:definadentrodeunbloquePL/SQLunatablaIndexByquealmacenelos DocumentosdelosEstudiantesexistentesenlabasededatosdesarrolladaenlaClase8.
DECLARE CURSOR C_ESTUDIANTE IS SELECT * FROM ESTUDIANTE; TYPE TABLA_APELLIDOS_TIPO IS TABLE OF ESTUDIANTE.APELLIDO_ESTUDIANTE%TYPE INDEX BY BINARY_INTEGER; TABLA_APELLIDOS TABLA_APELLIDOS_TIPO;

Observecomoprimerosedefineeltipodedatoquesealmacenarenlatablaenestecaso elmismotipodedatoempleadoparaalmacenarlosapellidosdelosestudiantesenlatabla estudianteyluegosecreaunatabladeesetipo.

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 111 de 121

LalneaINDEXBYespecificaeltipodedatoqueseemplearcomondicedelatabla.Esto seentiendemejorsisevisualizalatabla: TABLA_APELLIDOS

Imaginequeencadaunadelasfilasazulesseralmacenadounapellido,usteddebecontar conunndicequelepermitaaccederalasdiferentesfilas,paraelcasodeesteejemplose estdefiniendounndicedeltipoBINARY_INTEGERquenoesotracosaqueunenteroque vadesde2147483647hasta2147483647. Ejemplo2:complementeelejemplo1paraquelatablatipoIndexBydefinidaalmacenelos ApellidosdelosEstudiantes


DECLARE CURSOR C_ESTUDIANTE IS SELECT * FROM ESTUDIANTE; TYPE TABLA_APELLIDOS_TIPO IS TABLE OF ESTUDIANTE.APELLIDO_ESTUDIANTE%TYPE INDEX BY BINARY_INTEGER; TABLA_APELLIDOS TABLA_APELLIDOS_TIPO; INDICE_TABLA BINARY_INTEGER := 0; BEGIN FOR R_ESTUDIANTE IN C_ESTUDIANTE LOOP INDICE_TABLA := INDICE_TABLA + 1 ; TABLA_APELLIDOS(INDICE_TABLA) := R_ESTUDIANTE.APELLIDO_ESTUDIANTE; END LOOP; END;

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 112 de 121

Ejemplo3:complementeelejemplo2paraimprimircadaunadelasfilasalmacenadasenla tablaTABLA_APELLIDOS.
DECLARE CURSOR C_ESTUDIANTE IS SELECT * FROM ESTUDIANTE; TYPE TABLA_APELLIDOS_TIPO IS TABLE OF ESTUDIANTE.APELLIDO_ESTUDIANTE%TYPE INDEX BY BINARY_INTEGER; TABLA_APELLIDOS TABLA_APELLIDOS_TIPO; INDICE_TABLA BINARY_INTEGER := 0; BEGIN FOR R_ESTUDIANTE IN C_ESTUDIANTE LOOP INDICE_TABLA := INDICE_TABLA + 1; TABLA_APELLIDOS(INDICE_TABLA) := R_ESTUDIANTE.APELLIDO_ESTUDIANTE; END LOOP; --IMPRIMIR EL CONTENIDO DE LA TABLA INDEX BY FOR i IN 1..INDICE_TABLA LOOP PL(TABLA_APELLIDOS(i)); END LOOP; END; . /

LastablasIndexBygeneranunaexcepcintipoDATA_NO_FOUNDcuandointenta accederseaunvalordelndicenodefinido.Considereelsiguientecambioenelcdigo anterior:


DECLARE CURSOR C_ESTUDIANTE IS SELECT * FROM ESTUDIANTE; TYPE TABLA_APELLIDOS_TIPO IS TABLE OF ESTUDIANTE.APELLIDO_ESTUDIANTE%TYPE INDEX BY BINARY_INTEGER; TABLA_APELLIDOS TABLA_APELLIDOS_TIPO; INDICE_TABLA BINARY_INTEGER := 1;
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 113 de 121

BEGIN FOR R_ESTUDIANTE IN C_ESTUDIANTE LOOP TABLA_APELLIDOS(INDICE_TABLA) := R_ESTUDIANTE.APELLIDO_ESTUDIANTE; INDICE_TABLA := INDICE_TABLA + 1; END LOOP; --IMPRIMIR EL CONTENIDO DE LA TABLA INDEX BY FOR i IN 1..INDICE_TABLA LOOP PL(TABLA_APELLIDOS(i)); END LOOP; END; . /

Almomentodeejecutarestecdigoapareceelsiguienteerror:ORA-01403: no data found, laraznesqueelndicedelatablafueincrementadohastaunvalormayoralnmerodefilas quecontiene.Observecomoparacadaapellidoqueseguardaserealizaunincrementoenel valordelndice,estoimplicaquealmomentodealmacenarelltimoapellidoexistenteel ndiceserincrementadonuevamente,unvalorporencimadelnmeroderegistrosreal.Al momentodeimprimirlosapellidoselLoopdefinidollegarhastaelltimovalordelndicey cuandointenteimprimirdichoapellido,encontrarquenoexistendatosenlgenerandola excepcinpreviamentemencionada. 9.1.2TablasAnidadas:unatablaanidadaesmuyparecidaaunatabladeltipoIndexBy, peroadiferenciadestasnollevaelIndexBy.Lasintaxisparadefinirlaeslasiguiente: TYPEtype_nameISTABLEOFelement_type[NOTNULL]; table_nameTYPE_NAME; OtroaspectoquesedebetenerencuentaalmomentodetrabajarconTablasAnidadases questasdebeninicializarseantesdepoderguardarinformacinenellas. Ejemplo4:reescribaelEjemplo3empleandounaTablaAnidadaenlugardeunaTabla Asociativa.
DECLARE CURSOR C_ESTUDIANTE IS SELECT * FROM ESTUDIANTE; TYPE TABLA_APELLIDOS_TIPO IS TABLE OF ESTUDIANTE.APELLIDO_ESTUDIANTE %TYPE;
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 114 de 121

TABLA_APELLIDOS TABLA_APELLIDOS_TIPO := TABLA_APELLIDOS_TIPO();-inicializacion de la tabla INDICE_TABLA BINARY_INTEGER := 0; BEGIN FOR R_ESTUDIANTE IN C_ESTUDIANTE LOOP INDICE_TABLA := INDICE_TABLA + 1; TABLA_APELLIDOS.EXTEND();--se incrementa el tamao de la tabla TABLA_APELLIDOS(INDICE_TABLA) := R_ESTUDIANTE.APELLIDO_ESTUDIANTE; END LOOP; --IMPRIMIR EL CONTENIDO DE LA TABLA INDEX BY FOR i IN 1..INDICE_TABLA LOOP PL(TABLA_APELLIDOS(i)); END LOOP; END; . /

9.2MtodosdelasColecciones: EnelejemploanteriorseutilizelmtodoEXTENDdelatablaanidadaparair incrementandoeltamaodelamismaacadapasodentrodelLoop.Lascoleccionescuentan conlossiguientesmtodosparasumanipulacin: Nombre EXISTS Descripcin Disponibleen

DevuelveTRUEsiel IndexBy/Nested elementoespecificadoexiste enlacoleccin Devuelveelnmerode elementosdeunacoleccin IndexBy/Nested

COUNT EXTEND DELETE

Incrementaen1eltamaode Nested lacoleccin Eliminaunoovarios IndexBy/Nested elementosdeunacoleccin. Lasfilasdondeantesse encontrabanesoselementos quedandesocupadaspero


Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 115 de 121

libresparaserutilizadas despus FIRST LAST PRIOR Devuelveelprimerelemento IndexBy/Nested deunacoleccin Devuelveelltimoelemento deunacoleccin IndexBy/Nested

Devuelveelndicedel IndexBy/Nested elementon1aunaposicinn especfica Devuelveelndicedel IndexBy/Nested elementon+1aunaposicin nespecfica Eliminaunoomselementos Nested delfinaldeunacoleccin,las posicionesqueantes ocupabanestoselementos tambinsoneliminadas

NEXT

TRIM

Ejemplo5:Enunrestaurantedecomidasrpidasseencuentraungrupodepersonas haciendofilaenesperadeseratendidas,laconfiguracindelacolaeslasiguiente:

DondeLuisocupaelprimerpuestoyPedroseencuentraenlaltimaposicin. EscribaelcdigoPL/SQLquerecreeestasituacinempleandounaTablaAnidada,imprima elcontenidodelatablaparasuverificacin:


DECLARE TYPE PERSONA IS TABLE OF VARCHAR2(20); COLA_RESTAURANTE PERSONA := PERSONA(); --INICIALIZAR TABLA POSICION BINARY_INTEGER := 0;

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 116 de 121

BEGIN POSICION := POSICION + 1; COLA_RESTAURANTE.EXTEND(); COLA_RESTAURANTE(POSICION) POSICION := POSICION + 1; COLA_RESTAURANTE.EXTEND(); COLA_RESTAURANTE(POSICION) POSICION := POSICION + 1; COLA_RESTAURANTE.EXTEND(); COLA_RESTAURANTE(POSICION) POSICION := POSICION + 1; COLA_RESTAURANTE.EXTEND(); COLA_RESTAURANTE(POSICION) POSICION := POSICION + 1; COLA_RESTAURANTE.EXTEND(); COLA_RESTAURANTE(POSICION)

:= 'Luis'; --El primero en la cola

:= 'Adriana';

:= 'Jose';

:= 'Maria';

:= 'Pedro'; --El ultimo en la cola

FOR i IN 1..POSICION LOOP PL('Puesto: '||i||' '||COLA_RESTAURANTE(i)); END LOOP; END; . /

Ejercicio1:EmpleandolosmtodosPRIORyNEXTdelascoleccionesimprimaenpantalla lainformacinsobrelaspersonasqueseencuentranpordelanteypordetrsdeJose,utilice elcdigoanteriorcomopuntodepartida. SolucinPropuesta:


DECLARE TYPE PERSONA IS TABLE OF VARCHAR2(20); COLA_RESTAURANTE PERSONA := PERSONA(); --INICIALIZAR TABLA POSICION BINARY_INTEGER := 0; BEGIN POSICION := POSICION + 1; COLA_RESTAURANTE.EXTEND(); COLA_RESTAURANTE(POSICION) := 'Luis'; --El primero en la cola POSICION := POSICION + 1; COLA_RESTAURANTE.EXTEND();
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 117 de 121

COLA_RESTAURANTE(POSICION) POSICION := POSICION + 1; COLA_RESTAURANTE.EXTEND(); COLA_RESTAURANTE(POSICION) POSICION := POSICION + 1; COLA_RESTAURANTE.EXTEND(); COLA_RESTAURANTE(POSICION) POSICION := POSICION + 1; COLA_RESTAURANTE.EXTEND(); COLA_RESTAURANTE(POSICION)

:= 'Adriana';

:= 'Jose';

:= 'Maria';

:= 'Pedro'; --El ultimo en la cola

FOR i IN 1..POSICION LOOP PL('Puesto: '||i||' '||COLA_RESTAURANTE(i)); END LOOP; PL('Adelante de Jose se encuentra: '|| COLA_RESTAURANTE(COLA_RESTAURANTE.PRIOR(3))); PL('Detras de Jose se encuentra: '|| COLA_RESTAURANTE(COLA_RESTAURANTE.NEXT(3))); END; . /

Ejercicio2:EmpleandolosmtodosFIRSTyLASTdelascoleccionesimprimaenpantallala informacinsobrelaspersonasqueseencuentranenlaprimerayenlaltimaposicindela cola,utiliceelcdigoanteriorcomopuntodepartida. SolucinPropuesta:


DECLARE TYPE PERSONA IS TABLE OF VARCHAR2(20); COLA_RESTAURANTE PERSONA := PERSONA(); --INICIALIZAR TABLA POSICION BINARY_INTEGER := 0; BEGIN POSICION := POSICION + 1; COLA_RESTAURANTE.EXTEND(); COLA_RESTAURANTE(POSICION) := 'Luis'; --El primero en la cola POSICION := POSICION + 1; COLA_RESTAURANTE.EXTEND(); COLA_RESTAURANTE(POSICION) := 'Adriana';
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 118 de 121

POSICION := POSICION + 1; COLA_RESTAURANTE.EXTEND(); COLA_RESTAURANTE(POSICION) := 'Jose'; POSICION := POSICION + 1; COLA_RESTAURANTE.EXTEND(); COLA_RESTAURANTE(POSICION) := 'Maria'; POSICION := POSICION + 1; COLA_RESTAURANTE.EXTEND(); COLA_RESTAURANTE(POSICION) := 'Pedro'; --El ultimo en la cola FOR i IN 1..POSICION LOOP PL('Puesto: '||i||' '||COLA_RESTAURANTE(i)); END LOOP; PL('Adelante de Jose se encuentra: '|| COLA_RESTAURANTE(COLA_RESTAURANTE.PRIOR(3))); PL('Detras de Jose se encuentra: '|| COLA_RESTAURANTE(COLA_RESTAURANTE.NEXT(3))); PL('El primero en la cola es: '|| COLA_RESTAURANTE(COLA_RESTAURANTE.FIRST())); PL('El ultimo en la cola es: '|| COLA_RESTAURANTE(COLA_RESTAURANTE.LAST())); END; . /

9.3Tablasderegistros: Elconceptoexpuestoanteriormentepuedeextendersedelasiguientemanera:
TYPE TABLA_ESTUDIANTES_TIPO IS TABLE OF ESTUDIANTE.%ROWTYPE INDEX BY BINARY_INTEGER; TABLA_APELLIDOS TABLA_ESTUDIANTES_TIPO;

Ladefinicinanteriorpermiteentenderlascoleccionesdentrodelcontextoenelqueseha venidotrabajandoyhacerusodeunatcnicadenominadaBulkCollect. EsimportanteentenderquecadavezquedentrodeunprogramaPL/SQLserealizan consultasalabasededatoselcontrolpasadelmotorPL/SQLalpropiomotorSQL.Estono essignificativosiseesthablandode15o20registros,perocuandoseesthablandodeun nmeromayoresteiryvenirentreunmotoryotroreduceconsiderablementeeldesempeo


Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 119 de 121

deunprogramaPL/SQL. BulkCollectesunatcnicaquehaciendousodelastablasPL/SQLpermitetenerdentrodel motorPL/SQLlainformacinrequeridaparatrabajardentrodeunprograma,funcino procedimiento. Ejemplo1:utilizandounatablaIndexBy(asociativa)yBulkCollectrecuperelosregistrosde losestudiantesexistentesenlatablaESTUDIANTE,imprimacadaunodelosregistrosdela tabla.


DECLARE CURSOR C_ESTUDIANTE IS SELECT * FROM ESTUDIANTE; TYPE TIPO_ESTUDIANTE IS TABLE OF ESTUDIANTE%ROWTYPE INDEX BY BINARY_INTEGER; TABLA_ESTUDIANTES TIPO_ESTUDIANTE; V_CONTADOR BINARY_INTEGER := 0; BEGIN OPEN C_ESTUDIANTE; FETCH C_ESTUDIANTE BULK COLLECT INTO TABLA_ESTUDIANTES; FOR i IN 1..TABLA_ESTUDIANTES.COUNT() LOOP PL(TABLA_ESTUDIANTES(i).ID_ESTUDIANTE||CHR(9)|| TABLA_ESTUDIANTES(i).DOCUMENTO_ESTUDIANTE||CHR(9)|| TABLA_ESTUDIANTES(i).NOMBRE_ESTUDIANTE||CHR(9)|| TABLA_ESTUDIANTES(i).APELLIDO_ESTUDIANTE); END LOOP; END; . /

EsimportantetenerencuentaqueBULKCOLLECTpuedeaumentarelrendimientodelos programasPL/SQLsiempreycuandoelnmeroderegistrosquesealmacenenenmemoria delmotorPL/SQLnoseademasiadogrande,porloqueparatrabajarcontablasmuy grandesserecomiendausarLIMITNcomouncondicionaldelaseleccin:


DECLARE CURSOR C_ESTUDIANTE IS
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 120 de 121

SELECT * FROM ESTUDIANTE; TYPE TIPO_ESTUDIANTE IS TABLE OF ESTUDIANTE%ROWTYPE INDEX BY BINARY_INTEGER; TABLA_ESTUDIANTES TIPO_ESTUDIANTE; V_CONTADOR BINARY_INTEGER := 0; BEGIN OPEN C_ESTUDIANTE; FETCH C_ESTUDIANTE BULK COLLECT INTO TABLA_ESTUDIANTES LIMIT 3; FOR i IN 1..TABLA_ESTUDIANTES.COUNT() LOOP PL(TABLA_ESTUDIANTES(i).ID_ESTUDIANTE||CHR(9)|| TABLA_ESTUDIANTES(i).DOCUMENTO_ESTUDIANTE||CHR(9)|| TABLA_ESTUDIANTES(i).NOMBRE_ESTUDIANTE||CHR(9)|| TABLA_ESTUDIANTES(i).APELLIDO_ESTUDIANTE); END LOOP; END; . /

Ejercicio1:comparelasalidadelosdoscdigospresentadosanteriormente.Incluyaun Loopenelsegundoparaqueimprimatodoslosestudiantesalmismotiempoquerealizaun manejorazonabledememoriautilizandoelcondicionalLIMIT SolucinPropuesta:


DECLARE CURSOR C_ESTUDIANTE IS SELECT * FROM ESTUDIANTE; TYPE TIPO_ESTUDIANTE IS TABLE OF ESTUDIANTE%ROWTYPE INDEX BY BINARY_INTEGER; TABLA_ESTUDIANTES TIPO_ESTUDIANTE; V_CONTADOR BINARY_INTEGER := 0; BEGIN OPEN C_ESTUDIANTE;
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 121 de 121

LOOP FETCH C_ESTUDIANTE BULK COLLECT INTO TABLA_ESTUDIANTES LIMIT 3; EXIT WHEN(C_ESTUDIANTE%NOTFOUND); FOR i IN 1..TABLA_ESTUDIANTES.COUNT() LOOP PL(TABLA_ESTUDIANTES(i).ID_ESTUDIANTE||CHR(9)|| TABLA_ESTUDIANTES(i).DOCUMENTO_ESTUDIANTE||CHR(9)|| TABLA_ESTUDIANTES(i).NOMBRE_ESTUDIANTE||CHR(9)|| TABLA_ESTUDIANTES(i).APELLIDO_ESTUDIANTE); END LOOP; END LOOP; END; . /

Findelosapuntesdeclase

Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

También podría gustarte