Está en la página 1de 121

Programacin en Oracle con PL/SQL - 1 de 121

ProgramacinenOracleconPL/SQL
ApuntesdeClase

por:JosAndrsMartnezSilva

ElaboradosenFebreroMarzodel2009

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

Programacin en Oracle con PL/SQL - 2 de 121

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

Programacin en Oracle con PL/SQL - 3 de 121

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

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

Programacin en Oracle con PL/SQL - 4 de 121

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

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

Programacin en Oracle con PL/SQL - 5 de 121

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

Programacin en Oracle con PL/SQL - 6 de 121

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

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

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

Programacin en Oracle con PL/SQL - 7 de 121

SolucinPropuesta:
-------

indices para las tablas


CREATE [UNIQUE] INDEX <index_name>
on
<table_name> (
<column_name_1>,
<column_name_2>,
<column_name_N> );

CREATE UNIQUE INDEX alumno_ik1


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

Inserteahoralossiguientesdatosenlastablas:
id_alumno

documento_alumno

nombre_alumno

apellido_alumno

79799331

Jose

Martinez

53783975

Paola

Hernandez

id_leccion

fecha

17Feb09

18Feb09

id_examen

descripcion_examen

Primerarevision

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

Programacin en Oracle con PL/SQL - 8 de 121

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)

Programacin en Oracle con PL/SQL - 9 de 121

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

Programacin en Oracle con PL/SQL - 10 de 121

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

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

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

Programacin en Oracle con PL/SQL - 11 de 121

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

IngreseahoralossiguienteregistrosempleandoloscdigosDDLcorrespondientes:
id_alumno

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)

Programacin en Oracle con PL/SQL - 12 de 121

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

Quocurrecuandoseintentainsertarelsegundoregistro?
1.2ConsultassobrelaInformacinAlmacenada:
SELECT:seemplealainstruccinSELECTpararecuperarinformacindeunatablaoun
conjuntodetablas.
Consultasbsicasdeejemplo:
Recuperarlascalificacionesobtenidasporunalumnoespecficoalolargodelcurso:
SELECT * FROM EXAMEN_ALUMNO WHERE ID_ALUMNO = 1

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

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

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

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

Programacin en Oracle con PL/SQL - 13 de 121

Deberaobteneralgocomolosiguiente:

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

Verunreportemsadecuado:

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

YestaconsultasepodraconvertirenunaVista,quedeacuerdoconlaspalabrasdelautor
dellibrogua1puededefinirsecomo:

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

Programacin en Oracle con PL/SQL - 14 de 121

1.3Vistas:
AviewrepresentsthedefinitionofaSQLquery(SELECTstatement)asthoughitwerejust
anothertableinthedatabase.Hence,youcanINSERTintoandUPDATE,DELETE,and
SELECTfromaviewjustasyoucananytable...
Laformadedefinirunavistaeslasiguiente:
CREATE[ORREPLACE]VIEW<view_name>AS
<sql_select_statement>;
Deacuerdoconlocuallavistadeseadapuededefinirsedelasiguientemanera:
CREATE OR REPLACE VIEW vista_reporte1 AS
SELECT EA.ID_EXAMEN, EA.FECHA, EA.CALIFICACION, A.DOCUMENTO_ALUMNO,
A.NOMBRE_ALUMNO, A.APELLIDO_ALUMNO
FROM EXAMEN_ALUMNO EA JOIN
ALUMNO A
ON
EA.ID_ALUMNO = A.ID_ALUMNO;

UPDATE:estainstruccinseempleacuandosedeseaactualizarunregistroexistenteenuna
tabla.Suformageneraleslasiguiente:
UPDATE<table_name>
SET<column_name_1>=<column_value_1>,
<column_name_2>=<column_value_2>,...
<column_name_N>=<column_value_N>;
ActualizarlacalificacindelalumnoJuanPerez(id3)enelexamenpresentadoel18Feb09
(id1):
UPDATE EXAMEN_ALUMNO
SET CALIFICACION = 3
WHERE ID_ALUMNO = 3 AND ID_EXAMEN = 1;

Luegodeejecutarestaconsultapuedeconsultarselavistacreadaanteriormenteconelfinde
verreflejadosloscambios:
SELECT * FROM VISTA_REPORTE1;

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

Programacin en Oracle con PL/SQL - 15 de 121

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

LaseccindelcdigoqueseencuentraresaltadaseconocecomounSUBQUERY.
Cuandosedeseaeliminarunregistroexistenteenunatablaseemplealainstruccin
DELETE.
Notaalpie:cuandosedeseeeliminarunaconstraintpreviamentedefinidasepuedeemplear
lasiguienteinstruccin:
ALTER TABLE <nombre_tabla>
DROP CONSTRAINT <nombre_constraint>

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

Dichoalumnosepuedeeliminarconlainstruccin:
DELETE FROM ALUMNO WHERE ID_ALUMNO = 4;

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

Programacin en Oracle con PL/SQL - 16 de 121

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

Supongaahoraquequeremosmostrarenpantallalainformacinextradayrealizarsobrela
mismaalgntipodeoperacin.

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

Programacin en Oracle con PL/SQL - 17 de 121

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

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

Supongaahoraqueseingresaunid_alumnoquenoseencuentraenlabasededatos,en
estecasonohabrinformacinquemostraryesmuyrecomendablemanejarla
excepcinquesepresenta.

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

Programacin en Oracle con PL/SQL - 18 de 121

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

2.1Variablesdesustitucin:
Unaprimeraformadeobtenerinformacinporpartedelusuarioparaluegoutilizarladentro
deunbloqueoprogramaPL/SQLesempleandovariablesdesustitucin.
Ejercicio1:
EscribaenPL/SQLunbloquequepresentelamundialmentefamosafrase:HolaMundoen
maysculas
DECLARE
aux_hola varchar2(50) := 'Hola Mundo';
BEGIN
DBMS_OUTPUT.PUT_LINE(upper(aux_hola));
END;
/

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

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

Programacin en Oracle con PL/SQL - 19 de 121

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

Enelcasoanterior&nombre_usuarioesunavariabledesustitucin,suvalorsersolicitado
alusuariocuandoseejecuteelbloquePL/SQLyelvaloringresadoserempleadoenlas
SentenciasdeEjecucin.
SQL*Plus:parapodertrabajarconelbloqueanterioresnecesariohacerusodelaconsola
SQL*Plus,unclienteenmodotextoqueproveeOracle2parainteractuarconlabasededatos.
Laprimerainstruccinquedebeejecutarsetanprontocomoselanzaelclientees:
connectsystem/password@localhostconloqueseestablecelaconexinalservidorde
basesdedatos.Unavezconectadoalservidorescribaelcdigoanterioryobserveel
resultadodelaejecucin:
SQL> Enter value for nombre_usuario: jose andres
old
3: aux_nombre varchar2(50) := '&nombre_usuario';
new
3: aux_nombre varchar2(50) := 'jose andres';
PL/SQL procedure successfully completed.

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

Programacin en Oracle con PL/SQL - 20 de 121

Notaalgunadiferencia?
Bydefault,SQL*PLUSdoesn'treadwhataPL/SQLprogramhaswrittenwithdbms_output.
Withsetserveroutputon,thisbehaviourischanged.
Ejercicio1:
EscribaunprogramaPL/SQLquesolicitealusuarioeldocumentodeunalumno,consultesu
informacinenlabasededatosypresentesunombreyapellidojuntoconlacalificacin
obtenidaenelexamencuyoides1.Encasodequeeldocumentoingresadonocoincidacon
lainformacindeningnestudiantedebelanzarseunaexcepcinqueinformealusuariode
lanoexistenciadedatosparaesedocumento.
SET SERVEROUTPUT ON;
DECLARE
documento varchar2(20) := '&documento_ingresado';
id_examen number := 1;
aux_nombre varchar2(50);
aux_apellido varchar2(50);
aux_calificacion number;
BEGIN
SELECT A.NOMBRE_ALUMNO, A.APELLIDO_ALUMNO, EA.CALIFICACION
INTO aux_nombre, aux_apellido, aux_calificacion
FROM ALUMNO A
JOIN EXAMEN_ALUMNO EA
ON A.ID_ALUMNO = EA.ID_ALUMNO
WHERE A.DOCUMENTO_ALUMNO LIKE documento;
DBMS_OUTPUT.PUT_LINE('Informacion Solicitada: '||aux_nombre||' '||
aux_apellido||' '||aux_calificacion);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No existe informacion para el documento: '||
documento);
END;
.
/

Paraevitarlasalidaenpantalladelvalororiginalyelvalorsustituidosemodificaelcdigode
lasiguientemanera:

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

Programacin en Oracle con PL/SQL - 21 de 121

SET VERIFY OFF;


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

Elsmbolo.IndicaelfinaldeunbloquePL/SQLempleandoSQL*Plus
PreguntatipoCertificacin:
Quocurrealejecutarelsiguientecdigo:
SET SERVEROUTPUT ON;
DECLARE
v_var1 NUMBER(2) := 123;
v_var2 NUMBER(3) := 123;
v_var3 NUMBER(5,3) := 123456.123;
BEGIN
DBMS_OUTPUT.PUT_LINE('v_var1: '||v_var1);
DBMS_OUTPUT.PUT_LINE('v_var2: '||v_var2);
DBMS_OUTPUT.PUT_LINE('v_var3: '||v_var3);
END;

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

Programacin en Oracle con PL/SQL - 22 de 121

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

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

Programacin en Oracle con PL/SQL - 23 de 121

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

Estecambioeneltipodelasvariablesutilizadas,garantizarquesienunfuturosecambia
porejemploeltamaooeltipodedatodelcampoparaNOMBRE_ALUMNOelprogramaPL/
SQLsigafuncionandosinnecesidaddemodificarloenabsoluto.

2.2TiposdedatossoportadosenPL/SQL:
Declaracin

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)

Programacin en Oracle con PL/SQL - 24 de 121

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

2.3Bloquesanidadosycontroldeflujo:
Pararealizarlossiguientesejerciciosesnecesarioingresarlossiguientesdatosenlastablas
correspondientes:
LECCION:
id_leccion

Fecha

19Feb09

EXAMEN
id_examen

descripcion_examen

2doexamen

EXAMEN_LECCION:
id_examen

id_leccion

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

Programacin en Oracle con PL/SQL - 25 de 121

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;

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

Programacin en Oracle con PL/SQL - 26 de 121

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

Programacin en Oracle con PL/SQL - 27 de 121

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

> 0 AND (aux_sumatoria/aux_numero_examenes) >=


ALUMNO VA REPROBANDO EL CURSO HASTA EL
encuentran examenes presentados por ese alumno
EXISTEN REGISTROS PARA EL DOCUMENTO: '||

EXCEPTION --observe como nunca se llega a esta excepcion


WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('NO EXISTEN REGISTROS PARA EL DOCUMENTO: '||
aux_documento);
END;
.

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

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

Programacin en Oracle con PL/SQL - 28 de 121

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

Programacin en Oracle con PL/SQL - 29 de 121

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

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

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

Programacin en Oracle con PL/SQL - 30 de 121

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

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

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

Programacin en Oracle con PL/SQL - 31 de 121

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

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

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

Programacin en Oracle con PL/SQL - 32 de 121

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

Ejercicio:
DefinaunanuevafuncinenPL/SQLllamadacreate_alumnoquerecibacomoparmetrosel
documento,nombreyapellidodeunalumno,creeelregistrocorrespondienteenlatabla
ALUMNOyretorneunvalordeTRUEsilaoperacinsepudorealizaroFALSEencaso
contrario.
SolucinPropuesta:
CREATE OR REPLACE FUNCTION create_alumno(documento IN VARCHAR2, nombre IN
VARCHAR2, apellido IN VARCHAR2)
RETURN BOOLEAN
IS
BEGIN
INSERT INTO ALUMNO
VALUES (seq_alumno.NEXTVAL,documento,nombre,apellido);
RETURN TRUE;
EXCEPTION
WHEN OTHERS THEN
RETURN FALSE;--se produjo un error
END;

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

Programacin en Oracle con PL/SQL - 33 de 121

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

2.6Commit,RollbackySavepoint:
AunquesehamencionadoenunprrafoanteriorelusodeSETAUTOCOMMITONcomo
unaalternativaparaobviarelcomportamientotransaccionaldeOracle,enrealidadeste
comportamientodebeintegrarsealosprogramaselaboradosenPL/SQL
Lasintaxisbsicadeestostrescomandoseslasiguiente:
COMMIT[WORK]:hacepermanentesloscambiosdefinidosenunatransaccin,elparmetro
WORKesopcional
ROLLBACK[WORK]:deshaceloscambiosquesehabandefinidoenlatransaccinanterior,
elparmetroWORKesopcional
SAVEPOINTNAME:defineunpuntoapartirdelcualsepuedendeshacerloscambiosconel
comandoROLLBACK,cualquiercambioquesehayadefinidoantesdelSAVEPOINTnoseve
afectadoporelROLLBACK.
Ejemplo1:
Dadalatablaleccinconelsiguientecontenido:
ID_LECCIONFECHA

117FEB09
218FEB09
319FEB09
Inserteunregistroparalafecha23FEB09recuerdequesustablascuentanahoraconuna
secuenciaparalageneracinautomticadelallaveprimaria:
INSERT INTO leccion
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 34 de 121

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

2 ;

DefinaunpuntodeguardadoSAVEPOINT A;
Inserteunnuevoregistroparalafecha24Feb09yluegodeshagalosltimoscambios
empleando:ROLLBACK TO A;
Quinformacintienelatablaleccion?:
select * from leccion;
ID_LECCION FECHA
---------- -----------------1 17-FEB-09
2 18-FEB-09
3 19-FEB-09
4 23-FEB-09

CierresusesinconSQL*Plusyvuelvaainiciarlaqudatoscontieneahoralatabla
leccion?.

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

Programacin en Oracle con PL/SQL - 35 de 121

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

3
4
5
6

John Doe
Marcus Indigus
Roy Barnes
Louis Mint
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 36 de 121

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

UstedpuedeguardarestearchivodetextoeinvocarlodesdelaconsoladeSQL*Plus
empleandounasintaxissimilaralasiguiente(porfavortengaencuentalaestructurade
archivosdesusistemaoperativo):
@/home/jamslug/revisionParcial01.sql

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

Programacin en Oracle con PL/SQL - 37 de 121

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

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

Programacin en Oracle con PL/SQL - 38 de 121

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

UstedpuedeguardarestearchivodetextoeinvocarlodesdelaconsoladeSQL*Plus
empleandounasintaxissimilaralasiguiente(porfavortengaencuentalaestructurade
archivosdesusistemaoperativo):
@/home/jamslug/revisionParcial02.sql

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

Programacin en Oracle con PL/SQL - 39 de 121

3.ProcedimientosAlmacenados:
Demanerasimilaracomosedefinenfuncionesparasualmacenamientoyposteriorre
utilizacinenPL/SQLpuedendefinirseprocedimientos.Ladiferenciafundamentalentreuna
funcinyunprocedimientoesqueesteltimonoRETORNAningnvalor.Lasintaxisgeneral
paradefinirunprocedimientoeslasiguiente:
CREATE [OR REPLACE] PROCEDURE <procedure_name> [(
<parameter_name_1>
[IN] [OUT] <parameter_data_type_1>,
<parameter_name_2>
[IN] [OUT] <parameter_data_type_2>,...
<parameter_name_N>
[IN] [OUT] <parameter_data_type_N> )] IS
--the declaration section
BEGIN
-- the executable section
EXCEPTION
-- the exception-handling section
END;
/

Ejemplo1:NowyoullcreateaprocedurethatwrapstheSYS.DBMS_OUTPUT.put_line()
procedure,butusesaveryshortname.Youllendupusingthe
SYS.DBMS_OUTPUT.put_line()procedurealot.Itgetstiresometotypea24character
methodnameeverytimeyouwanttodisplayalineoftextonthescreeninSQL*Plus.So,to
savekeystrokes,youwillgiveyourSYS.DBMS_OUTPUT.put_line()wrapperprocedurethe
namepl(),asinpforputandlforline.tomadodellibrogua
CREATE OR REPLACE PROCEDURE pl(cadena IN VARCHAR2)
IS
BEGIN
DBMS_OUTPUT.PUT_LINE(cadena);
END;
.
/
--TESTING UNIT
DECLARE
v_aux VARCHAR2(500) := 'Hola alias para la funcion de impresion de
linea';
BEGIN
pl(v_aux);
pl('Este es el primer procedimiento almacenado');
pl(123);
END
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 40 de 121

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

--se determina el numero de examenes presentados por ese alumno en


particular
--segundo bloque
BEGIN
SELECT COUNT(EA.ID_EXAMEN) INTO aux_numero_examenes
FROM EXAMEN_ALUMNO EA
JOIN ALUMNO A
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 41 de 121

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

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

Programacin en Oracle con PL/SQL - 42 de 121

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

Programacin en Oracle con PL/SQL - 43 de 121

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

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

Programacin en Oracle con PL/SQL - 44 de 121

@/home/jamslug/date-spec.sql

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

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

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

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

Programacin en Oracle con PL/SQL - 45 de 121

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

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

Programacin en Oracle con PL/SQL - 46 de 121

if SQLCODE16 <> -183917 then


pl(SQLERRM18);
end if;
end;
end loop;
return d_random;
end random;

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

-- Write up the help text here in this help method


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

Programacin en Oracle con PL/SQL - 47 de 121

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

Programacin en Oracle con PL/SQL - 48 de 121

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

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

Programacin en Oracle con PL/SQL - 49 de 121

pl('ERROR: get_max() is not equal to d_MAX');


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

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

Programacin en Oracle con PL/SQL - 50 de 121

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

Programacin en Oracle con PL/SQL - 51 de 121

@/home/jamslug/par_o_impar_1.sql

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

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

Quunidaddepruebapuedeemplearparaverificarestafuncin?
4.2CASEconcondicionesdebsqueda:
Esposibletambinemplearelcasesobrebsquedasquedevuelvenresultadosbooleanos:
TRUE,FALSE,NULL.Enestecasolasintaxisaempleareslasiguiente:
CASE
WHENSEARCHCONDITION1THENSTATEMENT1;
WHENSEARCHCONDITION2THENSTATEMENT2;
...
WHENSEARCHCONDITIONNTHENSTATEMENTN;
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 52 de 121

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

4.3ControldeIteracionesconLOOP:
Enocasionessedeseeaqueunprogramarealicevariasveceslamismatarea,paralograr
estodebendefinirseLOOPS.EnOraclehaycuatrotiposdiferentesdeLOOPS,seexpondrn
acontinuacinlos3primeros.
4.3.1LoopSimple:enprincipioelLOOPmassimplequesepuededeclarartieneestaforma:
LOOP
STATEMENT 1;
STATEMENT 2;
...
STATEMENT N;
END LOOP;

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

Programacin en Oracle con PL/SQL - 53 de 121

EXIT;
END IF;
END LOOP;

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

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

Programacin en Oracle con PL/SQL - 54 de 121

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

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

Programacin en Oracle con PL/SQL - 55 de 121

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

Programacin en Oracle con PL/SQL - 56 de 121

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

Ejercicio1:escribaunprocedimientoPL/SQLquecalculelasumadelosnmerosdel1al
10yvayamostrandoacadapasoelvalordelasumaparcial.Amaneradeejemplose
presentanlasprimeraslneasquedebemostrarenpantallaesteprocedimiento:
1
3
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 57 de 121

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

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

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

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

Programacin en Oracle con PL/SQL - 58 de 121

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

ThereisnoneedtodefinetheloopcounterinthedeclarationsectionofthePL/SQLblock.
Thisvariableisdefinedbytheloopconstruct.lower_limitandupper_limitaretwointeger
numbersorexpressionsthatevaluatetointegervaluesatruntime,andthedoubledot(..)
servesastherangeoperator.
Ejercicio1:escribaunafuncinquepermitacalcularelfactorialdeunnmero.Recuerde
queelfactorialde5sedefinecomo5!=1*2*3*4*5
SolucinPropuesta:
CREATE OR REPLACE FUNCTION factorial(p_numero IN NUMBER)
RETURN NUMBER
IS
v_resultado NUMBER := 1;
BEGIN
FOR i IN 1..p_numero LOOP
v_resultado := v_resultado*i;
END LOOP;
RETURN v_resultado;
END;
.
/
@/home/jamslug/factorial.sql

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

Programacin en Oracle con PL/SQL - 59 de 121

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

Programacin en Oracle con PL/SQL - 60 de 121

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

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

Programacin en Oracle con PL/SQL - 61 de 121

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

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

Programacin en Oracle con PL/SQL - 62 de 121

END PARAMETERS;
.
/

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

Programacin en Oracle con PL/SQL - 63 de 121

pl('valor de la variable v_name luego de llamar a la funcion: '||


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

Ejercicio1:compileelpaqueteparametersyejecutesuprocedimientodepruebamediante
lainstruccinEXECUTEPARAMETERS.test,analicelasalidarecuerdedefinirlavariable
SERVEROUTPUTenON

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

Programacin en Oracle con PL/SQL - 64 de 121

6.ManejodeExcepciones:
Paradesarrollarestaparteemplearemosunafuncindivision(n1,n2)quedevolverelvalor
dedividirn1entren2.Elcdigodelamismaeselsiguiente:
--division.sql
CREATE OR REPLACE FUNCTION DIVISION(N1 IN NUMBER, N2 IN NUMBER)
RETURN NUMBER
IS
BEGIN
RETURN(N1/N2);
END;
.
/

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

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

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

Programacin en Oracle con PL/SQL - 65 de 121

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

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

6.1ExcepcionesComunesIncluidasenOracle:
ExistendiferentesexcepcionesincluidasenOraclequepermitencontrolarlascondicionesde
errormscomunes,lasiguientetablapresentaunresumendeestas:
Excepcin

Origen

Observacin

NO_DATA_FOUND

Estaexcepcinsepresenta
cuandoalrealizaruna
consulta(SELECToSELECT
INTO)noseobtieneningn
valor

EstaexcepcinNOocurre
cuandoserealizaunSELECT
COUNToSELECTSUM,
porquecualquieradeestas
dosfuncionesretornaun0en
casodenoexistirregistros

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

Programacin en Oracle con PL/SQL - 66 de 121

TOO_MANY_ROWS

Aparececuandoalrealizar
unaconsultaconSELECT
INTOelresultadocontiene
msdeunafila

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

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

Programacin en Oracle con PL/SQL - 67 de 121

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

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

Programacin en Oracle con PL/SQL - 68 de 121

SQL> EXECUTE CONSULTAR_ALUMNO(007);


No existen calificaciones para el Codigo: 7
PL/SQL procedure successfully completed.

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

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

Programacin en Oracle con PL/SQL - 69 de 121

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

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

Programacin en Oracle con PL/SQL - 70 de 121

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

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

Programacin en Oracle con PL/SQL - 71 de 121

--otras excepciones que se puedan presentar


WHEN OTHERS THEN
raise_application_error(-20002, SQLERRM);
END;
.
/

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

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

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

7.1.1Records:
UnRECORDesuntipodedatocompuesto,puedeestarasociadoaunatabla,aun
CURSORoserdefinidodirectamenteporelprogramador.

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

Programacin en Oracle con PL/SQL - 72 de 121

ParadeclararunRecordbasadoenunatablaseemplealasiguientesintaxis:
record_name table_name%ROWTYPE

SisedeseaqueelRecordestbasadoenunCURSORseemplealasiguientesintaxis:
record_name cursor_name%ROWTYPE

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

UstedpuedeentoncesusardeformaconjuntaunCURSORyunRECORDparaprocesar
conjuntosdedatosenlugardedatosindependientes.
Ejemplo2:EscribaunbloquePL/SQLquecumplalassiguientestareas:
1. DefinaunCURSORquerecuperartodoslosregistrosdelatablaSIMULAparaun
CODIGOespecfico
2. DEFINAUNRECORDasociadoalatablaSIMULAenelquepuedairseprocesandola
informacinreferenciadaporelCURSORanterior
3. Presenteenpantallacadaunodelosregistrosobtenidos
SolucinPropuesta:
--ejemplo basico de trabajo con CURSORES explicitos
DECLARE
CURSOR c_grades IS --declaracion del CURSOR
SELECT * FROM SIMULA
WHERE CODIGO = 46135886;
vr_info SIMULA%ROWTYPE; --declaracion de un record del tipo tabla: SIMULA
BEGIN
OPEN c_grades; --1. Abrir el Cursor
LOOP

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

Programacin en Oracle con PL/SQL - 73 de 121

FETCH c_grades INTO vr_info; --2. Recuperar la informacion del CURSOR


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

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

Ejercicio2:compileelanteriorbloquePL/SQLempleandounCODIGOqueseencuentreen
sutablaSIMULA,ejecteloyanalicelosresultadosobtenidos.

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

Programacin en Oracle con PL/SQL - 74 de 121

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

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

Programacin en Oracle con PL/SQL - 75 de 121

Ejercicio4:quocurreconelprocedimientoanteriorcuandoseingresauncdigoqueno
existeenlatabla?
7.2AtributosdelosCURSORES:
LossiguientessonlosatributosdeloscursoresquepuedenusarseenunprogramaPL/SQL
Nombre

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)

Programacin en Oracle con PL/SQL - 76 de 121

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

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

Programacin en Oracle con PL/SQL - 77 de 121

CLOSE c_infoalumno; --3.Close Cursor


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

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

Programacin en Oracle con PL/SQL - 78 de 121

WHILE (c_infoalumno%FOUND) LOOP


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

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

Programacin en Oracle con PL/SQL - 79 de 121

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

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

Programacin en Oracle con PL/SQL - 80 de 121

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

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

Programacin en Oracle con PL/SQL - 81 de 121

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

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

Programacin en Oracle con PL/SQL - 82 de 121

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

Programacin en Oracle con PL/SQL - 83 de 121

4. NotaMnimaobtenidaentodaslasmaterias(definidacomolacalificacinms
bajaobtenidaentodaslasevaluacionesDETODOSLOSCURSOS
presentadasporestealumno)
SolucinPropuesta:
Punto1y2:
--archivo para la creacion de tablas del modelo
--requerido para la parte final del curso
DROP
DROP
DROP
DROP
DROP

TABLE
TABLE
TABLE
TABLE
TABLE

ESTUDIANTE_EVALUACION;
CURSO_ESTUDIANTE;
EVALUACION;
CURSO;
ESTUDIANTE;

CREATE TABLE ESTUDIANTE(


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

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

Programacin en Oracle con PL/SQL - 84 de 121

CREATE TABLE EVALUACION(


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

Programacin en Oracle con PL/SQL - 85 de 121

ADD CONSTRAINT EEVAL_FKC1


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

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

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

Programacin en Oracle con PL/SQL - 86 de 121

INSERT INTO CURSO


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

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

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

Programacin en Oracle con PL/SQL - 87 de 121

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

Programacin en Oracle con PL/SQL - 88 de 121

.
/

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

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

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


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

Programacin en Oracle con PL/SQL - 89 de 121

SELECT * FROM EVALUACION


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

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

Programacin en Oracle con PL/SQL - 90 de 121

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

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

Programacin en Oracle con PL/SQL - 91 de 121

--DADO UN ID_ESTUDIANTE RECUPERAR EL VALOR MAXIMO


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

Programacin en Oracle con PL/SQL - 92 de 121

SELECT MAX(CALIFICACION) AS NOTAMAXIMA


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

FUNCTION NOTA_MEDIA_CURSO(VID_CURSO IN CURSO.ID_CURSO%TYPE)


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

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

Programacin en Oracle con PL/SQL - 93 de 121

END NOTA_MEDIA_CURSO;

FUNCTION NOTA_MINIMA_CURSO(VID_CURSO IN CURSO.ID_CURSO%TYPE)


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

FUNCTION NOTA_MAXIMA_CURSO(VID_CURSO IN CURSO.ID_CURSO%TYPE)


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

Programacin en Oracle con PL/SQL - 94 de 121

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

FUNCTION NOTA_MAXIMA_ESTUDIANTE(VID_ESTUDIANTE IN
ESTUDIANTE.ID_ESTUDIANTE%TYPE)
RETURN NUMBER
IS
BEGIN
OPEN C_EVALUACIONESEST1(VID_ESTUDIANTE);
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 95 de 121

FETCH C_EVALUACIONESEST1 INTO R_EVALUACIONESEST1;


CLOSE C_EVALUACIONESEST1;
RETURN R_EVALUACIONESEST1.NOTAMAXIMA;
END NOTA_MAXIMA_ESTUDIANTE;

PROCEDURE INFO_CURSO(VID_CURSO IN CURSO.ID_CURSO%TYPE)


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

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

Programacin en Oracle con PL/SQL - 96 de 121

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

PROCEDURE TEST
IS
BEGIN
END TEST;
*/
END COURSES;
.
/
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 97 de 121

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

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

Programacin en Oracle con PL/SQL - 98 de 121

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

TestUnit(unaimagenvalemasquemilpalabras):

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

Programacin en Oracle con PL/SQL - 99 de 121

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

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

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

Programacin en Oracle con PL/SQL - 100 de 121

COMMIT; -- Luego del commit el bloqueo sobre los registros termina y


otros usuarios/programas pueden modificarlos
EXCEPTION
WHEN NO_DATA_FOUND THEN
PL('NO EXISTE UN CURSO CON ESE CODIGO');
END;

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

Programacin en Oracle con PL/SQL - 101 de 121

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

ParahabilitarunTriggerseemplealasiguienteinstruccin:
ALTER TRIGGER trigger_name ENABLE;

DemanerasimilarparadeshabilitarunTriggerseemplealasiguienteinstruccin:
ALTER TRIGGER trigger_name DISABLE;

34 Data Manipulation Language


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

Programacin en Oracle con PL/SQL - 102 de 121

Sisedeseaasociarmsdeuntriggeralamismatablaesposibleemplearlaclusula
FOLLOWSparaindicarelordenenquedebenejecutarselosTriggers37:
ALTER TRIGGER trigger_name FOLLOWS trigger_name238;

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

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

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

Programacin en Oracle con PL/SQL - 103 de 121

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

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

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

Programacin en Oracle con PL/SQL - 104 de 121

DROP SEQUENCE SEQ_ESTADISTICA;


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

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

AhoraalconsultarlatablaESTADISTICAseobtienelainformacinrelacionadaconesta
operacin.

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

Programacin en Oracle con PL/SQL - 105 de 121

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

Ejercicio2:definaunTriggerqueguardeenlatablaESTADISTICApreviamentemodificada
lainformacinrelacionadaconlaoperacinINSERTqueserealizasobrelatabla
ESTUDIANTE.EsteTriggerdebedispararseDESPUESdequelaoperacindeINSERCION
serealiza.

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

Programacin en Oracle con PL/SQL - 106 de 121

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

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

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

Programacin en Oracle con PL/SQL - 107 de 121

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

Programacin en Oracle con PL/SQL - 108 de 121

INSERT INTO ESTADISTICA


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

Ahoralaejecucindeunaoperacincomo:INSERT INTO ESTUDIANTE


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

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

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

Programacin en Oracle con PL/SQL - 109 de 121

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

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

Consultelatabla.AhoraentiendeelfuncionamientodeunTriggerdeltipoInsteadOf.

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

Programacin en Oracle con PL/SQL - 110 de 121

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

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

Observecomoprimerosedefineeltipodedatoquesealmacenarenlatablaenestecaso
elmismotipodedatoempleadoparaalmacenarlosapellidosdelosestudiantesenlatabla
estudianteyluegosecreaunatabladeesetipo.

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

Programacin en Oracle con PL/SQL - 111 de 121

LalneaINDEXBYespecificaeltipodedatoqueseemplearcomondicedelatabla.Esto
seentiendemejorsisevisualizalatabla:
TABLA_APELLIDOS

Imaginequeencadaunadelasfilasazulesseralmacenadounapellido,usteddebecontar
conunndicequelepermitaaccederalasdiferentesfilas,paraelcasodeesteejemplose
estdefiniendounndicedeltipoBINARY_INTEGERquenoesotracosaqueunenteroque
vadesde2147483647hasta2147483647.
Ejemplo2:complementeelejemplo1paraquelatablatipoIndexBydefinidaalmacenelos
ApellidosdelosEstudiantes
DECLARE
CURSOR C_ESTUDIANTE IS
SELECT * FROM ESTUDIANTE;
TYPE TABLA_APELLIDOS_TIPO IS TABLE OF ESTUDIANTE.APELLIDO_ESTUDIANTE%TYPE
INDEX BY BINARY_INTEGER;
TABLA_APELLIDOS TABLA_APELLIDOS_TIPO;
INDICE_TABLA BINARY_INTEGER := 0;
BEGIN
FOR R_ESTUDIANTE IN C_ESTUDIANTE LOOP
INDICE_TABLA := INDICE_TABLA + 1 ;
TABLA_APELLIDOS(INDICE_TABLA) := R_ESTUDIANTE.APELLIDO_ESTUDIANTE;
END LOOP;
END;

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

Programacin en Oracle con PL/SQL - 112 de 121

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

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

Programacin en Oracle con PL/SQL - 113 de 121

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

Almomentodeejecutarestecdigoapareceelsiguienteerror:ORA-01403: no data found,


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

Programacin en Oracle con PL/SQL - 114 de 121

TABLA_APELLIDOS TABLA_APELLIDOS_TIPO := TABLA_APELLIDOS_TIPO();-inicializacion de la tabla


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

9.2MtodosdelasColecciones:
EnelejemploanteriorseutilizelmtodoEXTENDdelatablaanidadaparair
incrementandoeltamaodelamismaacadapasodentrodelLoop.Lascoleccionescuentan
conlossiguientesmtodosparasumanipulacin:
Nombre

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

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

Programacin en Oracle con PL/SQL - 115 de 121

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;

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

Programacin en Oracle con PL/SQL - 116 de 121

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

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

:= 'Adriana';

:= 'Jose';

:= 'Maria';

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

FOR i IN 1..POSICION LOOP


PL('Puesto: '||i||' '||COLA_RESTAURANTE(i));
END LOOP;
END;
.
/

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

Programacin en Oracle con PL/SQL - 117 de 121

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

:= 'Adriana';

:= 'Jose';

:= 'Maria';

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

FOR i IN 1..POSICION LOOP


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

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

Programacin en Oracle con PL/SQL - 118 de 121

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

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

Ladefinicinanteriorpermiteentenderlascoleccionesdentrodelcontextoenelqueseha
venidotrabajandoyhacerusodeunatcnicadenominadaBulkCollect.
EsimportanteentenderquecadavezquedentrodeunprogramaPL/SQLserealizan
consultasalabasededatoselcontrolpasadelmotorPL/SQLalpropiomotorSQL.Estono
essignificativosiseesthablandode15o20registros,perocuandoseesthablandodeun
nmeromayoresteiryvenirentreunmotoryotroreduceconsiderablementeeldesempeo
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 119 de 121

deunprogramaPL/SQL.
BulkCollectesunatcnicaquehaciendousodelastablasPL/SQLpermitetenerdentrodel
motorPL/SQLlainformacinrequeridaparatrabajardentrodeunprograma,funcino
procedimiento.
Ejemplo1:utilizandounatablaIndexBy(asociativa)yBulkCollectrecuperelosregistrosde
losestudiantesexistentesenlatablaESTUDIANTE,imprimacadaunodelosregistrosdela
tabla.
DECLARE
CURSOR C_ESTUDIANTE IS
SELECT * FROM ESTUDIANTE;
TYPE TIPO_ESTUDIANTE IS TABLE OF ESTUDIANTE%ROWTYPE
INDEX BY BINARY_INTEGER;
TABLA_ESTUDIANTES TIPO_ESTUDIANTE;
V_CONTADOR BINARY_INTEGER := 0;
BEGIN
OPEN C_ESTUDIANTE;
FETCH C_ESTUDIANTE BULK COLLECT INTO TABLA_ESTUDIANTES;
FOR i IN 1..TABLA_ESTUDIANTES.COUNT() LOOP
PL(TABLA_ESTUDIANTES(i).ID_ESTUDIANTE||CHR(9)||
TABLA_ESTUDIANTES(i).DOCUMENTO_ESTUDIANTE||CHR(9)||
TABLA_ESTUDIANTES(i).NOMBRE_ESTUDIANTE||CHR(9)||
TABLA_ESTUDIANTES(i).APELLIDO_ESTUDIANTE);
END LOOP;
END;
.
/

EsimportantetenerencuentaqueBULKCOLLECTpuedeaumentarelrendimientodelos
programasPL/SQLsiempreycuandoelnmeroderegistrosquesealmacenenenmemoria
delmotorPL/SQLnoseademasiadogrande,porloqueparatrabajarcontablasmuy
grandesserecomiendausarLIMITNcomouncondicionaldelaseleccin:
DECLARE
CURSOR C_ESTUDIANTE IS
Apuntes de Clase por Jos Andrs Martnez Silva (http://jamslug.com)

Programacin en Oracle con PL/SQL - 120 de 121

SELECT * FROM ESTUDIANTE;


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

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

Programacin en Oracle con PL/SQL - 121 de 121

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

Findelosapuntesdeclase

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

También podría gustarte