Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Guia Curso-JAMS-Version Final (Recuperado)
Guia Curso-JAMS-Version Final (Recuperado)
ProgramacinenOracleconPL/SQL
ApuntesdeClase
por:JosAndrsMartnezSilva
ElaboradosenFebreroMarzodel2009
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)
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
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);
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
descripcion_tema
id_leccion 1 1
/*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)
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)
) 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 );
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)
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
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)
Luegodeejecutarestaconsultapuedeconsultarselavistacreadaanteriormenteconelfinde verreflejadosloscambios:
SELECT * FROM VISTA_REPORTE1;
Ejercicio6: Insertarunnuevoalumnoconlossiguientesdatos:4,11111,Alumno,Eliminado
INSERT INTO ALUMNO VALUES(4,'11111','Alumno','Eliminado');
Dichoalumnosepuedeeliminarconlainstruccin:
DELETE FROM ALUMNO WHERE ID_ALUMNO = 4;
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.
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; /
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; /
DECLARE aux_hola varchar2(50) := 'Hola'; aux_nombre varchar2(50) := '&nombre_usuario'; BEGIN DBMS_OUTPUT.put_line(upper(aux_hola)||' '||upper(aux_nombre)); END; . /
Paraevitarlasalidaenpantalladelvalororiginalyelvalorsustituidosemodificaelcdigode lasiguientemanera:
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; . /
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)
EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('No existe informacion para el documento: '|| documento); END; . /
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
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_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
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
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)
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;
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:
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');
START WITH 2 INCREMENT BY 1 NOCACHE CREATE SEQUENCE seq_tema MINVALUE 3 START WITH 3 INCREMENT BY 1 NOCACHE
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)
2 ;
CierresusesinconSQL*Plusyvuelvaainiciarlaqudatoscontieneahoralatabla leccion?.
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
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;
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
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; . /
--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)
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;
-- 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; /
@/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;
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)
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)
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)
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)
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
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)
@/home/jamslug/par_o_impar_1.sql
UnidaddePrueba(TestUnit)paraelejemploanterior:
SELECT par_impar(4) FROM dual;
Sinembargo,definidodeestamanera,elLOOPnoacabaradeiterarjams.Esnecesario incluirunacondicinqueindiquelasalidadelLOOP:
LOOP STATEMENT 1; STATEMENT 2; IF CONDITION THEN
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)
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)
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)
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)
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; . /
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.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)
VALUES(SEQ_SIMULA.NEXTVAL,aux_codigo,ROUND(DBMS_RANDOM.VALUE(1,5),2)) ; END LOOP; --loop interno END LOOP; --loop externo END; . /
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;
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)
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; . /
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; . /
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
6.1ExcepcionesComunesIncluidasenOracle: ExistendiferentesexcepcionesincluidasenOraclequepermitencontrolarlascondicionesde errormscomunes,lasiguientetablapresentaunresumendeestas: Excepcin NO_DATA_FOUND Origen Estaexcepcinsepresenta cuandoalrealizaruna consulta(SELECToSELECT INTO)noseobtieneningn valor Observacin EstaexcepcinNOocurre cuandoserealizaunSELECT COUNToSELECTSUM, porquecualquieradeestas dosfuncionesretornaun0en casodenoexistirregistros
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
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; . /
SQL> EXECUTE CONSULTAR_ALUMNO(007); No existen calificaciones para el Codigo: 7 PL/SQL procedure successfully completed.
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.
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)
--otras excepciones que se puedan presentar WHEN OTHERS THEN raise_application_error(-20002, SQLERRM); END; . /
Ejemplo1:definauncursorquetraigatodaslascalificacionesasociadasauncdigodado
DECLARE CURSOR c_MyCursor IS SELECT CALIFICACION FROM SIMULA WHERE CODIGO = 5665789;
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;
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; . /
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)
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)
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;
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)
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)
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)
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)
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)
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);
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)
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
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;
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)
. /
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;
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)
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)
--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)
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);
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)
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;
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)
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;
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)
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):
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;
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;
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)
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'
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);
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.
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; . /
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); /
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)
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.
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;
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; . /
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)
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; . /
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:
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)
:= 'Adriana';
:= 'Jose';
:= 'Maria';
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';
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; . /
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;
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; . /
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