Documentos de Académico
Documentos de Profesional
Documentos de Cultura
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
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
);
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)
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.
SolucinPropuesta:
-------
Inserteahoralossiguientesdatosenlastablas:
id_alumno
documento_alumno
nombre_alumno
apellido_alumno
79799331
Jose
Martinez
53783975
Paola
Hernandez
id_leccion
fecha
17Feb09
18Feb09
id_examen
descripcion_examen
Primerarevision
id_tema
nombre_tema
descripcion_tema
presentacion
ModelamientoDB
id_examen
id_leccion
id_leccion
id_examen
id_alumno
fecha
calificacion
18Feb2009
18Feb2009
Cdigosugerido(DDL):
/*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
);
1.1PrimerejemplodeprogramacinconPL/SQL:Triggers
LosTriggerssonacciones(programas)queseejecutandeformaautomticacuandouna
accinserealizaovaarealizarsesobreunatabla(entidad)
Requisito:InsertarinformacinenlatablamediantelainstruccinINSERT
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
documento_alumno
nombre_alumno
apellido_alumno
989056789
Juan
Perez
id_examen
id_alumno
fecha
calificacion
18Feb09
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)
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
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)
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;
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;
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;
/
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.
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
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)
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:
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;
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)
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
CapacidadMxima
VARCHAR2(tamao)
4000bytes
CHAR[(tamao)]
2000bytes
Observacin
Sinosedefineuntamaoel
valorpordefectoesde1byte
(1caracter)
NUMBER[(#dgitos,
precisin)]
DATE
TIMESTAMP
Almacenalafecha+lahora
conunaprecisinde9dgitos
BOOLEAN
TRUE/FALSE/NULL
LONG
ExtiendeelVARCHAR2
permitiendoalmacenarhasta
2GB
LOB
LargeObject4GBde
almacenamiento.Seemplea
paraelalmacenamientode
objetosbinarioscomoVideos,
Imgenes,Audios,etc...
Whenyoureaprogrammer,itisimportanttoknowtheoperatorsthatyoucanuseina
programminglanguage.Theydetermineyourvariousoptionsforsolvingaprogrammatic
problem.ThefollowingaretheoperatorsyoucanuseinPL/SQL:
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)
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
Fecha
19Feb09
EXAMEN
id_examen
descripcion_examen
2doexamen
EXAMEN_LECCION:
id_examen
id_leccion
EXAMEN_ALUMNO:
id_examen
id_alumno
fecha
calificacion
23Feb09
23Feb09
1.5
23Feb09
2.5
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;
SELECTCOUNT(campo)permitedeterminarelnmerototalderegistrosquecumplancon
uncriterio,porejemploparadeterminarelnmerodecalificacionesdisponiblesparaun
alumnoenparticularpuedeejecutarseelsiguientecdigoSQL:
SELECT COUNT(ID_EXAMEN) FROM EXAMEN_ALUMNO WHERE ID_ALUMNO = 2;
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)
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;
/
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:
ElvalordelparmetroMINVALUEdebeser4.
MAXVALUE:definesiassedeseaelvalormximoquepodrtomarlasecuencia.En
casodenoespecificarningnvalorparaesteparmetrosuvalorpordefectoesde
999999999999999999999999999
STARTWITH:eselprimervalorqueseasignaalasecuencia,sisuvalornoseespecificael
mismocoincideconelMINVALUE
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)
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)
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 ;
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?.
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)
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
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;
.
/
UstedpuedeguardarestearchivodetextoeinvocarlodesdelaconsoladeSQL*Plus
empleandounasintaxissimilaralasiguiente(porfavortengaencuentalaestructurade
archivosdesusistemaoperativo):
@/home/jamslug/revisionParcial02.sql
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)
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;
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;
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)
-- 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;
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)
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)
FUNCTION start_of_day(
aid_date in date )
return date is
begin
return trunc19(aid_date);
end start_of_day;
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)
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)
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;
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)
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)
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)
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)
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;
.
/
Ejercicio1:escribaunprocedimientoPL/SQLquecalculelasumadelosnmerosdel1al
10yvayamostrandoacadapasoelvalordelasumaparcial.Amaneradeejemplose
presentanlasprimeraslneasquedebemostrarenpantallaesteprocedimiento:
1
3
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)
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;
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
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)
Ejercicio1:compileelpaqueteparametersyejecutesuprocedimientodepruebamediante
lainstruccinEXECUTEPARAMETERS.test,analicelasalidarecuerdedefinirlavariable
SERVEROUTPUTenON
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;
.
/
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
Origen
Observacin
NO_DATA_FOUND
Estaexcepcinsepresenta
cuandoalrealizaruna
consulta(SELECToSELECT
INTO)noseobtieneningn
valor
EstaexcepcinNOocurre
cuandoserealizaunSELECT
COUNToSELECTSUM,
porquecualquieradeestas
dosfuncionesretornaun0en
casodenoexistirregistros
TOO_MANY_ROWS
Aparececuandoalrealizar
unaconsultaconSELECT
INTOelresultadocontiene
msdeunafila
ZERO_DIVIDE
Aparecealintentaruna
divisinpor0
LOGIN_DENIED
Ocurrecuandounusuario
intentaconectarseconel
servidorOracleempleando
unnombredeusuarioy/ouna
contraseainvlidos
PROGRAM_ERROR
Aparececuandounprograma
PL/SQLcontieneunerror
VALUE_ERROR
Ocurrecuandounavariable
nopuedeconvertirsedeun
tipoaotro,ocuandoel
tamaodelamismanole
permitealmacenarelvalor
solicitado
DUP_VALUE_ON_INDEX
Ocurrecuandoseintenta
insertarunvalorduplicadoen
unacolumnaconrestriccin
devalornico
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;
.
/
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)
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;
.
/
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)
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.
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
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)
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;
.
/
Ejercicio4:quocurreconelprocedimientoanteriorcuandoseingresauncdigoqueno
existeenlatabla?
7.2AtributosdelosCURSORES:
LossiguientessonlosatributosdeloscursoresquepuedenusarseenunprogramaPL/SQL
Nombre
FormadeUso
Descripcin
%NOTFOUND
nombrecursor%NOTFOUND
EsteatributodevuelveTRUE
silaoperacinFETCH
inmediatamenteanteriorNO
recuperunregistrovlidoo
FALSEcuandoesposible
hacerlo
%FOUND
nombrecursor%FOUND
EsteatributodevuelveTRUE
silaoperacinFETCH
inmediatamenteanterior
recuperunregistrovlidoo
FALSEcuandoNOesposible
hacerlo
%ROWCOUNT
nombrecursor%ROWCOUNT Permiteconocerelnmerode
registrosquehansido
recuperadosconlas
operacionesFETCH
anteriores
%ISOPEN
nombrecursor%ISOPEN
DevuelveTRUEsielcursor
consultadoseencuentra
abierto,FALSEencaso
contrario
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;
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)
Esmuyimportantecerrarsiempreloscursoresexplcitoslosimplcitossoncerradospor
ORACLEautomticamentepuesdeestamaneraseliberanlosrecursosdememoria
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)
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)
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;
.
/
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)
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)
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;
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
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;
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)
END NOTA_MEDIA_CURSO;
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)
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;
PROCEDURE TEST
IS
BEGIN
END TEST;
*/
END COURSES;
.
/
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)
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;
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;
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)
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);
UnidaddePrueba:
Serealizalasiguienteactualizacinenelsistema:
UPDATE ESTUDIANTE_EVALUACION
SET CALIFICACION=CALIFICACION+0.5
WHERE ID_ESTUDIANTE=3
AND ID_EVALUACION=15;
AhoraalconsultarlatablaESTADISTICAseobtienelainformacinrelacionadaconesta
operacin.
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.
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:
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)
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)
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;
Observecomoprimerosedefineeltipodedatoquesealmacenarenlatablaenestecaso
elmismotipodedatoempleadoparaalmacenarlosapellidosdelosestudiantesenlatabla
estudianteyluegosecreaunatabladeesetipo.
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;
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)
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;
.
/
9.2MtodosdelasColecciones:
EnelejemploanteriorseutilizelmtodoEXTENDdelatablaanidadaparair
incrementandoeltamaodelamismaacadapasodentrodelLoop.Lascoleccionescuentan
conlossiguientesmtodosparasumanipulacin:
Nombre
Descripcin
Disponibleen
EXISTS
DevuelveTRUEsiel
IndexBy/Nested
elementoespecificadoexiste
enlacoleccin
COUNT
Devuelveelnmerode
elementosdeunacoleccin
EXTEND
Incrementaen1eltamaode Nested
lacoleccin
DELETE
Eliminaunoovarios
IndexBy/Nested
elementosdeunacoleccin.
Lasfilasdondeantesse
encontrabanesoselementos
quedandesocupadaspero
IndexBy/Nested
libresparaserutilizadas
despus
FIRST
Devuelveelprimerelemento IndexBy/Nested
deunacoleccin
LAST
Devuelveelltimoelemento
deunacoleccin
PRIOR
Devuelveelndicedel
IndexBy/Nested
elementon1aunaposicinn
especfica
NEXT
Devuelveelndicedel
IndexBy/Nested
elementon+1aunaposicin
nespecfica
TRIM
Eliminaunoomselementos Nested
delfinaldeunacoleccin,las
posicionesqueantes
ocupabanestoselementos
tambinsoneliminadas
IndexBy/Nested
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;
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';
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)
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';
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)
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)
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)
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)
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