MANUAL PL/SQL ORACLE
Qué es el lenguaje PL/SQL y primeras explicaciones para saber
cómo funciona este gestor PL/SQL.
La unidad de trabajo en PL/SQL es el bloque, constituido por un conjunto de declaraciones,
instrucciones y mecanismos de gestión de errores y excepciones.
Bloques
Con PL/SQL se pueden construir distintos tipos de programas: procedimientos, funciones y
bloques anónimos, paquetes, etc. Todos ellos tienen en común una estructura básica
denominada bloque.
Un bloque tiene 3 zonas:
Zona de declaraciones: donde se declaran objectos locales. Suele ir precedida por la
cláusula declare (o is o as en los procedimientos y funciones).
Un conjunto de instrucciones precedido por la cláusula BEGIN
Zona de excepciones: es una zona opcional precedida por la cláusula EXCEPTION, donde se
gestionan las excepciones.
El formato genérico de un bloque es el siguiente:
[ DECLARE
<declaraciones>]
BEGIN
<instrucciones>
[EXCEPTION
<gestión de excepciones>]
END;
Las únicas cláusulas obligatorias son BEGIN y END
Antes de hacer ningún bloque tenemos que ejecutar el siguiente comando en nuestra
ventana de Sql *PLUS
set serveroutput on;
ejemplo de un bloque
DECLARE
v_num_empleados number(2);
BEGIN
insert into depart values(99,'provisional',null);
update emple set dept_no=99 where dept_no=20;
v_num_empleados:=SQL%ROWCOUNT;
delete from depart where dept_no=20
DBMS_OUTPUT.PUT_LINE (v_num_empleados || 'Empleados cambiados a provisional');
Definición de datos compatibles con SQL
Este lenguaje suele tener unos tipos de datos compatibles con SQL para las columnas de las
tablas, pero además puede tener otros tipos de datos propios.
Para declarar los datos en un bloque tenemos que utilizar una sintaxis como esta:
DECLARE
nombre_variable Tipo dato;
BEGIN
...
Un ejemplo seria este:
DECLARE
precio NUMBER(8,2);
suma NUMBER(2) DEFAULT 0;
prenda CHAR(20) NOT NULL :='Camiseta';
descripción VARCHAR2(15);
BEGIN
....
Una de las ventajas de PL/SQL es que nos permite declarar una variable del mismo tipo que
otra variable o que una columna de una tabla. Esto lo hacemos con el atributo %TYPE.
DECLARE
nombre emple.nombre%TYPE;
Otra ventaja es que nos permite guardar el contenido de una fila entera de una tabla en una
variable. Esto lo hacemos con el atributo %ROWTYPE
DECLARE
mifila emple%ROWTYPE;
Con esto ya podemos trabajar con variables dentro de nuestro bloque. Ahora tenemos que
ver las estructuras de control que podemos manejar dentro de nuestros bloques.
Estructuras de control
Las estructuras de control son básicamente las mismas que podemos utilizar en cualquier
lenguaje de programación.
La vamos a dividir en estructuras de control alternativas (IF) y estructuras de control
repetitivas (WHILE, FOR, LOOP)
La estructura del IF seria la siguiente:
IF <condición> THEN
instrucciones
ELSEIF <condición> THEN
instrucciones
....
ELSE
instrucciones
END IF;
La estructura del WHILE seria la siguiente:
WHILE <condición> LOOP
instrucciones
END LOOP;
La estructura del FOR se escribiría así:
FOR <variable> IN <mínimo> <máximo> LOOP
instrucciones
END LOOP
Si queremos que nos vaya contando al revés, es decir de 5 hasta 0 por ejemplo, la sintaxis
seria la siguiente:
FOR <variable> IN REVERSE
<final>.......<inicio> LOOP
instrucciones
.....
END LOOP;
Y la estructura del LOOP seria de esta forma:
LOOP
instrucciones
....
EXIT WHEN <condición>
instrucciones
...
END LOOP;
Cursores implicitos
Es importante saber que en nuestros bloques PL/SQL es bastante práctico el uso de cursores.
En este lenguaje el resultado de una consulta no va directamente al terminal del usuario,
sino que se guarda en un área de memoria a la que se accede mediante los nombrados
cursores. Para realizar una consulta en PL/SQL tenemos que guardar el resultado en cursores.
Esto es muy sencillo y basta con meter un INTO en las consultas. Un ejemplo seria este:
select <columna/s> INTO <variable/s> from <tabla> [where]
select count(*) INTO vnum from ventas;
La variable que sigue al INTO recibe el valor de la columna. Por este motivo es importante
que el tipo de dato de la variable coincida con el tipo de dato de la columna.
Gestión de excepciones
Las excepciones sirven para tratar los errores y mensajes. Oracle tiene una serie de
excepciones que son las más frecuentes y con las que la mayoría de la gente trabaja.
Unas de las más usadas son:
NO_DATA_FOUND (cuando una orden tipo select no ha devuelto ningún valor)
TOO_MANY_ROWS (cuando una orden tipo select ha devuelto mas de una fila)
OTHERS THEN RAISE_APPLICATION_ERROR (para cualquier otro tipo de error desconocido)
Un ejemplo seria el siguiente:
DECLARE
vapellido varchar(10);
voficio varchar(20);
BEGIN
select apellido,oficio INTO vape,voficio from emple where emp=15;
DBMS_OUTPUT.PUT_LINE (vape||: - || voficio);
EXCEPTION
WHEN NO_DATA_FOUND THEN insert into temp values('No hay datos');
WHEN TOO_MANY_ROWS THEN insert into temp values ('Demasiados datos');
WHEN OTHER THEN RAISE_APPLICATION_ERROR(-2000,'Error en aplicación');
END;
Estructura modular
En PL/SQL podemos distinguir 3 tipos de programas o bloques.
Bloques anónimos: Son los que no tienen nombre y comienzan con el DECLARE, es decir los
que hemos ido viendo hasta ahora.
Procedimientos: Se trata del programa más utilizado en PL/SQL y su formato es el siguiente:
PROCEDURE <nombre_procedimiento>
[(<lista de parámetros>)]
IS
[<declaraciones>]
BEGIN
<instrucciones>;
[EXCEPTIONS
<excepciones>;]
END;
En el formato distinguimos dos partes claramente, la cabecera donde esta el nombre del
procedimiento y los parámetros y el cuerpo donde se situá un bloque anónimo.
Funciones: similares a los procedimientos pero con la función añadida de que pueden
devolver valores.
Si subís varias lineas y veis el ejemplo de control de excepciones, podéis ver que hemos
utilizado un atributo como DBMS_OUTPUT. Bien pues esto lo que nos permite es visualizar en
pantalla los resultados, tanto excepciones como mensajes. Lo utilizamos porque PL/SQL no
dispone de ordenes o sentencias que capturen datos introducidos por teclado, ni tampoco
para visualizar datos en pantalla.
DBMS_OUTPUT.PUT_LINE nos permite visualizar en pantalla, pero para que funcione
correctamente tenemos que poner el SET SERVEROUTPUT a ON
Si queremos que un bloque nos pida algún dato tenemos que anteponer el símbolo & delante
de la variable, de esta forma cuando el bloque llegue a ese punto nos pedirá por pantalla el
valor.
Otra sentencia importante es la que nos permite visualizar los errores que hemos podido
tener al crear el bloque o procedimiento. Esta sentencia es: show errors
Podemos agregar comentarios a nuestros bloques anteponiendo al comentario “/*
<comentario> */”
Si queremos que el bloque anónimo se ejecute directamente cuando terminamos de crearlo
debemos poner el símbolo / que, ademas de guardar el bloque en el buffer, lo ejecuta.
También podemos guardar los bloques anónimos en ficheros para poderlos ejecutar
posteriormente. Para ello ejecutamos la siguiente sentencia:
save nombrefichero
Y para ejecutarlo primero tenemos que cargar el fichero en el buffer y para ello tenemos que
ejecutar la siguiente sentencia:
get nombrefichero
Una vez cargado el fichero ejecutamos el bloque con la sentencia run nombrefichero.
O podemos hacer los dos pasos con una sola sentencia: start nombrefichero
Sin embargo para los procedimientos es totalmente distinto ya que al tener nombre se
almacena automáticamente en la base de datos y para ejecutarlo tan solo tenemos que
realizar la siguiente operación:
execute nombre_procedimiento(parámetros);
En el siguiente capitulo revisaremos todo lo que hemos visto en la introducción del PL/SQL,
pero de una forma mas exhaustiva.
Bloques anónimos PL/SQL
Empezaremos con los bloques anónimos, caracterizados porque no tienen nombre y se
suelen crear y ejecutar desde PL/SQL.
Todo bloque debe acabar en . para que sea almacenado en el buffer SQL. Una vez guardado
lo podemos ejecutar con la orden “run”. También podemos guardarlo en un fichero con la
siguiente orden:
save nombrefichero [replace]
El replace sólo lo pondremos si el fichero ya esta creado.
Para cargar y ejecutar este bloque anónimo guardado en fichero ejecutaremos la siguiente
orden:
start nombrefichero
El start lo podemos cambiar por la @ y nos funcionará igualmente.
Pero también podemos cargarlo sin ejecutarlo con la orden “get” y luego ejecutarlo
posteriormente con la orden “run”
Un ejemplo muy sencillo de bloque seria el que nos muestra en pantalla un nombre.
BEGIN
DBMS_OUTPUT.PUT_LINE('nombre');
END;
.
Además en los bloques PL/SQL se pueden utilizar lo que llamamos variables de sustitución,
que nos pedirán datos por pantalla antes de ejecutar el bloque. Estas variables tienen que ir
antepuestas del & para que funcionen.
Un ejemplo seria un bloque que nos pide el DNI de un usuario y nos muestra su nombre.
DECLARE
Vnom clientes.nombre%TYPE;
BEGIN
select nombre into Vnom from clientes where NIF= '&V_nif';
DBMS_OUTPUT.PUT_LINE (Vnom);
END;
.
Como veis es bastante sencillo, pero no tienen tanta funcionalidad como los procedimientos
o funciones.
Procedimientos y funciones PL/SQL
Los procedimientos y funciones quedan almacenados en la base de datos a diferencia de los
bloques anónimos que se almacenaban en el buffer.
Nota: Al quedar los bloques anónimos almacenados en el buffer, a no ser que se guardasen en ficheros,
se perderían al limpiar el buffer, cosa que no ocurre con los procedimientos y funciones, que se
almacenan en la propia base de datos.
Otra cosa que nos diferencia los bloques anónimos de los procedimientos o funciones es que
en los procedimientos o funciones no se pueden utilizar variables de sustitución.
En cuanto a su construcción es la dada en el articulo Características de PL/SQL segunda parte
añadiendo al principio la siguiente secuencia “CREATE OR REPLACE” para crearlo, o
modificarlo si ya existe.
Pasamos a escribir un procedimiento que nos muestre los datos de un usuario:
CREATE OR REPLACE PROCEDURE ver_usuario(nomusu VARCHAR2)
IS
NIFusu VARCHAR2(10);
Domusu VARCHAR2(10);
BEGIN
select nif, domicilio into NIFusu,Domusu from usuario where nombre=nomusu;
DBMS_OUTPUT.PUT_LINE('Nombre:'||nomusu|| 'NIF:' ||NIFusu|| 'Domicilio' ||Domusu);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No hemos encontrado al usuario || nomusu);
END;
/
Si el compilador detecta errores nos saldrá un mensaje como este: “Procedimiento creado
con errores de compilación”. Para ver estos errores tenemos la orden SHOW ERRORS.
Al tener almacenado el procedimiento en la base de datos, este puede ser llamado por
cualquier usuario que tenga los permisos oportunos. Para invocar un procedimiento
utilizamos la orden EXECUTE
Para invocar al procedimiento que hemos creado antes tendríamos que ejecutar la siguiente
orden:
EXECUTE ver_usuario('Luis');
Pero también podemos invocarlo desde un bloque PL/SQL de ls siguiente forma:
BEGIN
ver_usuario('Luis');
END;
.
Como en cualquier lenguaje, podemos agregar comentarios a nuestros procedimientos de la
siguiente forma:
- - para comentar en una sola linea
/* <comentario>*/ para varias lineas.
Tipos de datos
Este lenguaje dispone de los mismo tipos de datos que podemos encontrar en SQL, pero
además se han incorporado algunos nuevos:
char(n): almacena una cantidad fija de caracteres
varchar2(n): almacena cadenas de caracteres de longitudes variables
long(n): almacena cadenas de longitud variable
number(p,e): almacena numero de precisión p y escala e
boolean: almacena valores lógicos, true, false o NULL
date: almacena fechas completas, incluida la hora
raw(n): almacena datos binarios de longitud fija
long raw : almacena datos binarios de longitud variable
rowid: almacena identificadores de direcciones de fila
etc.
Además es importante señalar que el programador puede definir sus propios tipos de datos a
partir de los ya definidos.
Identificadores
Se utilizan para nombrar los objetos que intervienen en los programas PL/SQL como son las
variables, constantes, cursores, excepciones, procedimientos, etc.
Pueden tener como máximo 30 caracteres empezando siempre por una letra, que puede ir
seguida por otras letras, numeros, $, # ó _. Es importante destacar que PL/SQL no diferencia
entre mayúsculas y minúsculas. También debemos señalar que no pueden contener espacios
ni signos de puntuación.
Variables
Como doy por sentado que todos sabemos lo que son las variables, pasaremos directamente
a comentar como se declara una variable en PL/SQL.
<nombreVariable> <tipo> [NOT NULL] [{:= | DEFAULT } <valor>]
No podemos indicar una lista de variables del mismo tipo y luego declarar el tipo, tenemos
que hacerlo una a una.
Uno ejemplo de declaración de variables seria el siguiente:
DECLARE
importe NUMBER (8,2);
contador NUMBER(2'0);
nombre char(5) NOT NULL :="Sara";
...
Uso de los atributos %TYPE y %ROWTYPE
%TYPE: declara una variable del mismo tipo que otra, o que una columna de una tabla
%ROWTYPE : crea una variable registro cuyos campos se corresponden con las columnas de
una tabla o vista.
Por ejemplo si tenemos una variable definida previamente llamada cantidad podemos definir
otra de la siguiente forma:
total cantidad%TYPE;
De esta forma la variable total tendrá las mismas características que la variable cantidad.
Otro ejemplo seria declarar una variable que fuera del mismo tipo que la columna nombre de
la tabla profesor.
nombre_alta nombre%ROWTYPE;
Ámbito y visibilidad de variables
La variable será local para el bloque en el que ha sido declarada y global para los bloque
hijos de éste, mientras que las variables declaradas en los bloque hijos no son globales a los
bloques padre.
Constantes
Cómo en la mayoría de los lenguajes, en este también podemos declaras constantes, de la
siguiente forma:
<nombreVariable> CONSTANT <tipo> := <valor>;
Operadores
Asignación :=
LógicosANDOR
NOTConcatenación ||
Comparación
Is null = != <> < >
<= >=
between...and like in
y sus correspondientes negacionesAritméticos + - * / **
Funciones predefinidas
En PL/SQL tenemos las mismas funciones predefinidas que en SQL (AVG, MIN, MAX, COUNT,
SUM, etc), pero tenemos que tener dos cosas muy claras a la hora de utilizarlas y son:
1. La función no modifica el valor de las variables o expresiones que se pasan como
argumentos, sino que devuelve un valor a partir de dicho argumento.
2. Si a una función se le pasa un valor nulo en la llamada, posiblemente devolverá un
valor nulo.
Etiquetas
Podemos utilizar etiquetas para poder irnos a cualquier parte del programa utilizando la
sentencia GOTO siempre y cuando se cumplan las siguientes reglas:
1. No pueden haber etiquetas con los mismos nombres en un mismo programa.
2. La etiqueta debe preceder a un bloque o a un conjunto de ordenes ejecutables
3. La etiqueta no puede estar dentro de estructuras de control (IF, LOOP)
En cualquier subprograma podemos distinguir:
La cabecera, compuesta por el nombre del subprograma, los parámetros y el tipo de
valor de retorno.
El cuerpo, compuesto por las declaraciones, las instrucciones y el manejo de
excepciones.
Podemos distinguir entre dos tipos de subprogramas, como ya hemos comentado en artículos
anteriores:
Procedimientos
Los procedimientos ya los hemos visto en el articulo “Bloques anónimos y procedimientos
PL/SQL ” por lo que pasamos directamente a las funciones.
Funciones
Las funciones son muy similares a los procedimiento con la diferencia que éstas siempre
devolverán un valor. Su estructura es la siguiente:
CREATE [OR REPLACE] FUNCTION NombreFuncion [(parámetros)] RETURN tipo
IS [parte declarativa]
BEGIN
instrucciones
RETURN <valor o expresión>;
[EXCEPTION excepciones]
END;
La cláusula RETURN de la cabecera nos especifica el tipo de valor que nos va a devolver la
función.
Parámetros
Todos los subprogramas utilizan parámetros para pasar y recibir información.
Dentro de los parámetros podemos distinguir dos tipos:
Parámetros actuales: son variables indicadas en la llamada a un subprograma.
Parámetros formales: son variables declaradas en la especificación del subprograma.
Además podemos hacer el paso de parámetros de un tipo a otro. Generalmente si los tipos
son compatibles PL/SQL lo hace automáticamente. En cualquier caso, podemos hacerlo de
forma manual utilizando las siguientes notaciones:
Posicional: el compilador asocia los parámetros actuales a los formales, basándose
en suposición.
Nominal: el símbolo => después del parámetro actual y antes del nombre del formal,
indica al compilador correspondencia.
Mixta: nos permite usar las dos notaciones anteriores.
Para que esto quede más claro pasamos a escribir un ejemplo de paso de parámetros y
conversión de tipos.
Tenemos la especificación de un procedimiento como esta:
PROCEDURE departamento(
n_departamento INTEGER,
localidad VARCHAR2
IS...
Desde el siguiente bloque se podrán realizar las llamadas indicadas:
DECLARE
num_departamento INTEGER;
aula VARCHAR(30)
BEGIN
...
- - posicional departamento(num_departamento, aula);
- - nominal departamento(num_departamento => n_departamento, aula =>localidad);
...
END;
Esto nos pasaría los parámetros num_departamento al mismo tipo que n_departamento y
localidad al mismo tipo que aula.
Los parámetros que soporta PL/SQL pueden ser de entrada, salida o entrada/salida
IN
Nos permite pasar valores a un subprograma. Dentro del subprograma, el parámetro actuá como una
constante. Puede ser una variable, constante, literal o expresión.
OUT
Permite devolver valores al bloque que llamó al subprograma. Dentro del subprograma, el parámetro
actúa como una variable no inicializada. Solo puede ser una variable.
IN OUT
Permite pasar un valor inicial y devolver un valor actualizado. Dentro del subprograma, el parámetro
actuá como variable inicializada. Puede intervenir otras expresiones. El valor actual debe ser una variable.
El formato genérico es el siguiente:
<nombrevariable> [IN | OUT | IN OUT] <tipodato>
[ { := | DEFAULT} <valor>]
Además es importante recordar que al especificar parámetros debemos indicar el tipo, pero
no el tamaño.
Creación, modificación y borrado de subprogramas
Cuando creamos subprogramas con SQL * PLUS utilizando los comandos CREATE, Oracle
automáticamente compila el código fuente, genera el código objeto y los guarda en el
diccionario de datos, quedando disponibles para su utilización.
Para volver a compilar un subprograma almacenado utilizaremos la orden ALTER en vez del
CREATE y su formato es el siguiente:
ALTER {PROCEDURE | FUNCITON} nombresubprograma COMPILE;
Para ver el código de un subprograma almacenado podemos ejecutar una sentencia como
esta;
select LINE, SUBSTR(TEXT,1,60) from USER_SOURCE where name = 'nombresubprograma';
Para borrar un subprograma almacenado utilizaremos la orden DROP de la siguiente forma:
DROP {PROCEDURE | FUNCTION} nombresubprograma;
Nota: PL/SQL implementa la recursividad en los subprogramas, esto quiere decir, que un programa
puede llamarse a si mismo.
En los anteriores capítulos hemos visto los fundamentos del lenguaje PL/SQL, bien pues, a
partir de ahora pasaremos a estudiar el manejo de este lenguaje para trabar con el gestor de
Oracle. Empezaremos con la utilización de cursores.
Hasta ahora hemos utilizado cursores implícitos , cuando devolvíamos el resultado de una
select mediante la clausula into a una variable. Pero esto es un problema cuando el resultado
de una subconsulta nos devolvía varias filas, porque esto nos daria un error al ejecutar la
consulta
Para que no nos salte un error en estos casos debemos utilizar los cursores explícitos.
Cursores explícitos
Los cursores explícitos los utilizamos cuando tenemos consultas que nos devuelven
más de una fila.
Tenemos 4 operaciones básicas para trabajar con un cursor explícito.
1. Declaración del cursor: lo tenemos que declarar en la zona de declaraciones, con el
siguiente formato: CURSOR <nombrecursor> IS <sentencia SELECT>;
2. Apertura del cursor: Deberá colocarse en la zona de instrucciones, con el siguiente
formato:
OPEN <nombrecursor>;
Al hacerlo se ejecuta automáticamente la sentencia select y sus resultados se
almacenan en las estructuras internas de memoria manejadas por el cursor.
3. Recogida de información: Para recuperar la información anteriormente guardada en
las estructuras de memoria interna tenemos que usar el siguiente formato:
FETCH <nombrecursor> INTO {<variable> | <listavariables>};
Si tenemos una única variable que recoge los datos de todas las columnas, el
formato de la variable seria el siguiente:
<variable> <nombrecursor>%ROWTYPE;
Si tenemos una lista de variables, cada una recogerá la columna correspondiente de
la cláusula select, por lo que serán del mismo tipo que las columnas.
4. - Cierre del cursor:
CLOSE <nombrecursor>;
Ahora, veamos un ejemplo de utilización de cursores explícitos:
DECLARE
CURSOR C1 IS SELECT nombre, apellido FROM arbitro;
Vnom VARCHAR2(12);
Vape VARCHAR2(20);
BEGIN
OPEN C1;
LOOP
FETCH C1 INTO Vnom, Vape;
EXIT WHEN C1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(Vnom || '' || Vapen);
END LOOP;
CLOSE C1;
END;
Si nos fijamos, en la declaración de los cursores explícitos no utilizamos la cláusula INTO, que
sí se utilizaba en los cursores implícitos.
Además podéis ver que después del FETCH hemos comprobado si nos devuelve valores con
la línea del EXIT. Es algo importante ya que si no nos devuelve nada el LOOP se interrumpirá.
Atributos del cursor
Para conocer detalles de la situación del cursor tenemos 4 atributos:
%FOUND: devuelve verdadero si el último FETCH ha recuperado algún valor; en caso
contrario devuelve falso; si el cursor no está abierto nos devuelve error.
%NOTFOUND: hace justamente lo contrario al anterior.
%ROWCOUNT: nos devuelve el número de filas recuperadas hasta el momento.
%ISOPEN: devuelve verdadero si el cursor está abierto.
Veamos ahora un ejemplo de utilización de %ROWCOUNT:
DECLARE
CURSOR C1 IS SELECT nombre from futbolista WHERE Cod='e1';
Vnom VARCHAR2(15);
BEGIN
OPEN C1;
LOOP
FETCH C1 INTO Vnom;
EXIT WHEN C1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE (C1%ROWCOUNT || Vnom);
END LOOP;
CLOSE C1;
END;
Variables de acoplamientos en el manejo de cursores
En el ejemplo siguiente podemos observar que en la cláusula WHERE se incluye una variable
que se debería haber declarado previamente. Este tipo de variables reciben el nombre de
variables de acoplamiento. El programa la sustituirá por su valor en el momento en que se
abre el cursor, y se seleccionarán las filas según dicho valor. Aunque ese valor cambie
durante la recuperación de los datos con FETCH, el conjunto de filas que contiene el cursor
no variará.
El ejemplo nos muestra los futbolistas de un equipo cualquiera.
CREATE OR REPLACE PROCEDURE ver_futbolistas_por_equipos(codeq VARCHAR2)
IS
Vequi VARCHAR2(3);
CURSOR C1 IS SELECT nombre from futbolista where codeq=Vequi;
Vnom VARCHAR(15);
BEGIN
vequi:=codeq;
OPEN C1;
FETCH C1 INTO vnom;
WHILE C1%FOUND LOOP
DBMS_OUTPUT.PUT_LINE(Vnom);
FETCH C1 INTO Vnom;
END LOOP;
CLOSE C1;
END;
Variables de acoplamiento
Si os fijáis en el siguiente ejemplo veréis que en la cláusula where se incluye una variable
que se deberá declarar previamente. Este tipo de variables recibe el nombre de variables de
acoplamiento. El programa la sustituirá por su valor en el momento en que se abre el cursor,
y se seleccionarán las filas según dicho valor.
Create or replace procedure ver_jugador(codeq varchar2)
IS
vequi varchar2(3);
cursor c1 is select nombre from jugador where cod=vequi;
vnom varchar2(15);
BEGIN
vequi:=codeq;
OPEN c1;
FETCH c1 INTO vnom;
WHILE c1%found LOOP
DBMS_OUTPUT.PUT_LINE(vnom);
FETCH c1 INTO vnom;
END LOOP;
CLOSE c1;
END;
Cursor FOR …. LOOP
El trabajo normal de un cursor consiste en declarar un cursor, declarar una variable que
recogerá los datos del cursor, abrir el cursor, recuperar con fetch, una a una, las filas
extraídas introduciendo los datos en las variables, procesándolos y comprobando si se han
recuperado datos o no.
Para resumir todas esas tareas, tenemos una estructura cursor FOR...LOOP que hace todas
estas cosas de forma implícita, todas menos la declaración del cursor.
El formato y el uso de esta estructura es:
1. se declara la información cursor en la sección correspondiente
2. Se presenta el cursor utilizando el siguiente formato: FOR nombreVarReg IN
nombreCursor
LOOP …. END LOOP;
Al entrar en el bucle se abre el cursor de manera automática, se declara
implícitamente la variable nombreVarReg de tipo nombrecursor%ROWTYPE y se
ejecuta el primer fetch cuyo resultado quedarán en nombreVarReg. A continuación se
realizaran las acciones que correspondas hasta llegar al END LOOP.
Este es un ejemplo del LOOP …. END LOOP:
DECLARE
cursor c2 is select nombre, peso, estatura from jugador where salario>1200;
BEGIN
FOR vreg IN c2 LOOP
DBMS_OUTPUT.PUT_LINE (vreg.nombre || '-' ||vreg.peso || '-' || vreg.estatura);
END LOOP;
END;
Cursores con parámetros
Un cursor puede tener parámetros; en este caso se aplicara el siguiente formato genérico:
CURSOR nombrecursor [(parámetros)] IS SELECT <sentencia select en la que intervendrán
los parámetros>;
Los parámetros formales indicados después del nombre del cursor tienen la siguiente
sintaxis:
nombreCursor [IN] tipodato [{:=|DEFAULT} valor]
Todos los parámetros formales de un cursor son parámetros de entrada y su ámbito es local
al cursor por eso sólo pueden ser referenciados dentro de la consulta.
Un ejemplo seria el siguiente:
DECLARE
...
CURSOR C1 (vpeso number, vestatura number DEFAULT 170) is select nficha, nombre FROM
emple WHERE estatura=vestatura AND peso=vpeso;
Para abrir un cursor con parámetros lo haremos de la siguiente forma:
OPEN nombrecursor [(parámetros)];
Atributos con Cursores implícitos
Los atributos de los cursores implícitos que se crean son los siguientes:
SQL%NOTFOUND: nos dice si el último insert, update,delete o select into no han
afectado a ninguna fila.
SQL%FOUND: nos dice si el último insert, update,delete o select into ha afectado a
una o mas filas
SQL%ROWCOUNT: devuelve el número de filas afectadas por el último insert, update,
delete o select into
SQL%ISOPEN: Nos dice si el cursor esta cerrado, por lo que en teoría siempre nos
dará falso ya que Oracle cierra automáticamente el cursor después de cada orden
SQL.
Es importante tener en cuenta una serie de cosas:
Si se trata de un select into tenemos que tener en cuenta que solo puede devolver una única
fila de lo contrario nos levantará automáticamente una de estas dos excepciones:
NO_DATA_FOUND: si la consulta no devuelve ninguna fila
TOO_MANY_ROWS: si la consulta devuelve más de una fila
Cuando un select into hace referencia a una función de grupo nuca se levantará la excepción
NO_DATA_FOUND y SQL%FOUND siempre será verdadero. Esto se explica porque las
funciones de grupo siempre devuelven algún valor (NULL se considera un valor).
Uso de cursores para actualizar filas
Para realizar una actualización con un cursor tenemos que añadir la siguiente FOR UPDATE al
final de la declaración del cursor:
CURSOR nombre_cursor <declaraciones> FOR UPDATE
Esto indica que las filas seleccionadas por el cursor van a ser actualizadas o borradas. Una
vez declarado un cursor FOR UPDATE, se incluirá el especificador CURRENT OF
nombre_cursor en la cláusula WHERE para actualizar o borrar la última fila recuperada
mediante la orden FETCH.
{UPDATE|DELETE}... WHERE CURRENT OF nombre_cursor.
Os pongo un ejemplo para que quede claro:
Subir el salario a todos los empleados del departamento indicado en la llamada. El porcentaje
se indicará también en la llamada.
CREATE OR REPLACE PROCEDURE subir_salario (num_dept NUMBER, incre NUMBER)
IS
CURSOR c IS SELECT oficio, salario FROM empleados WHERE cod_dept=num_dept
FOR UPDATE;
reg c%ROWTYPE;
inc NUMBER (8);
BEGIN
OPEN c;
FETCH c INTO reg;
WHILE c%FOUND LOOP
inc :=(reg.salario/100 )* inc;
UPDATE empleados SET salario=salario+inc WHERE CURRENT OF c
FETCH c INTO reg;
END LOOP;
END;
También podemos usar ROWID en lugar de FOR UPDATE. ROWID nos indicará la fila que se va
a actualizar. Para ello, al declarar el cursor en la cláusula SELECT indicaremos que seleccione
también el identificador de fila:
CURSOR nombre_cursor IS SELECT columna1,columna2,...ROWID FROM tabla;
Al ejecutarse el FETCH se guardará el número de fila en una variable y después ese número
se podrá usar en la cláusula WHERE de la actualización:
{UPDATE |DELETE } ... WHERE ROWID = variable_rowid
El ejemplo anterior utilizando ROWID quedaría de la siguiente manera:
CREATE OR REPLACE PROCEDURE subir_salario (num_dept NUMBER, incre NUMBER)
IS
CURSOR c IS SELECT oficio, salario,ROWID FROM empleados WHERE cod_dept=num_dept
FOR UPDATE;
reg c%ROWTYPE;
inc NUMBER (8);
BEGIN
OPEN c;
FETCH c INTO reg;
WHILE c%FOUND LOOP
inc :=(reg.salario/100 )* inc;
UPDATE empleados SET salario=salario+inc WHERE ROWID =
reg.ROWID;
FETCH c INTO reg;
END LOOP;
END;
En este artículo del Manual de PL/SQL de Oracle vamos a ver lo que son las excepciones,
para qué sirven y cómo utilizarlas. Daremos un repaso también a los tipos de excepciones,
las excepciones definidas por el usuario y la sintaxis con la que tenemos que especificarlas.
Por último, de paso que vemos cosas acerca del tratamiento de errores en PL/SQL,
explicaremos el RAISE_APPLICATION_ERROR, un componente del sistema gestor de base de
datos Oracle que ayuda a gestionar errores y sus mensajes de error.
Qué son las excepciones en Oracle
Las excepciones, presentes en la mayoría de los lenguajes de programación, sirven para
tratar errores en tiempo de ejecución. En el sistema que nos ocupa, Oracle, sirven también
para definir qué se debe hacer frente a errores en sentencias definidas por el usuario.
Cuando se produce un error PL/SQL levanta una excepción y pasa el control a la sección
excepción correspondiente al bloque PL/SQL.
El formato sería el siguiente:
BEGIN
.........
......
......
EXCEPTION
WHEN <nombre_excepción> THEN
<instrucciones>;
......
[WHEN OTHERS THEN <instrucciones>;]
END;
Excepciones predefinidas
Son aquellas que se disparan automáticamente al producirse determinados errores. Estas
son las más comunes:
too_many_rows: Se produce cuando select … into devuelve más de una fila.
no_data_found: se produce cuando un select …. into no devuelve ninguna fila.
login_denied: error cuando intentamos conectarnos a Oracle con un login y clave no
validos.
not_logged_on: se produce cuando intentamos acceder a la base de datos sin estar
conectados.
program_error: se produce cuando hay un problema interno en la ejecución del programa.
value_error: se produce cuando hay un error aritmético o de conversión.
zero_divide: se puede cuando hay una división entre 0.
dupval_on_index: se crea cuando se intenta almacenar un valor que crearía duplicados en
la clave primaria o en una columna con restricción UNIQUE.
invalid_number: se produce cuando se intenta convertir una cadena a un valor numérico.
Hay alguna más pero estas son las más utilizadas y tenemos que tener en cuenta que no es
necesario declararlas en la sección DECLARE.
Excepciones definidas por el usuario
Son aquellas que crea el usuario. Para ello se requieren tres pasos:
1. Definición: se realiza en la zona de DECLARE con el siguiente formato:
nombre_excepción EXCEPTION
2. Disparar o levantar la excepción mediante la orden raise: RAISE ;
3. Tratar la excepción en el apartado EXCEPTION: WHEN THEN ;
Para que esto quede más claro ponemos un ejemplo a continuación.
DECLARE
...
Importe_mal EXCEPTION;
...
BEGIN
...
IF precio NOT BETWEEN mínimo and máximo THEN
RAISE importe_mal;
END IF;
...
EXCEPTION
WHEN importe_mal THEN DBMS_OUTPUT.PUT_LINE("Importe incorrecto");
...
END;
Otras excepciones
Existen otros errores internos de Oracle que no tienen asignada una excepción, sino un
código de error y un mensaje, a los que se accede mediante funciones SQLCODE y SQLERRM.
Cuando se produce un error de estos se trasfiere directamente el control a la sección
EXCEPTION donde se tratara el error en la clausula WHEN OTHERS de la siguiente forma:
WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Error'||SQLCODE||SQLERRM.)
Utilización de RAISE_APPLICATION_ERROR
En el paquete DBMS_STANDARD se incluye un procedimiento llamado
RAISE_APPLICATION_ERROR que nos sirve para levantar errores y definir mensajes de error.
Su formato es el siguiente:
RAISE_APPLICATION_ERROR(numero_error,mensaje_error);
Es importante saber que el número de error está comprendido entre -20000 y -20999 y el
mensaje es una cadena de caracteres de hasta 512 bytes.
Este procedimiento crea una excepción que solo puede ser tratada en WHEN OTHERS.
Ponemos un ejemplo para que nos quede más claro.
CREATE or REPLACE PROCEDURE subir_horas (emple NUMBER, horas_subir NUMBER)
IS
horas_actuales NUMBER;
BEGIN
Select horas into horas_actuales from empleados where id_empleado=emple;
if horas_actuales is NULL then
RAISE_APPLICATION_ERROR(-20010,'No tiene horas');
else
update empleados set horas=horas_actuales + horas_subir where id_empleado=emple;
end if;
End subir_horas;
Control de transacciones en Oracle. Una transacción se define como
un conjunto de operaciones sobre la base de datos.
En Oracle si se ejecuta un conjunto de operaciones y una de ellas falla se aborta la
transacción entera. En este artículo veremos todo lo que debemos saber sobre transacciones
y algunos ejemplos interesantes.
La transacción finaliza cuando se ejecuta un comando de control de transacciones como
rollback o commit work (se puede abreviar poniendo simplemente commit).
Un ejemplo:
BEGIN
....
update alumnos set edad=20 where n_alumno=109;
update nuevos set apellido='perez' where n_alumno=200;
commit work;
...
EXCEPTION
WHEN OTHERS THEN
rollback work;
END;
Comandos utilizados para el control de transacciones
Commit
Este comando da por concluida la transacción actual y hace definitivos los cambios
realizados liberando las filas bloqueadas. Sólo después de que se ejecute commit tendremos
acceso a los datos modificados.
Rollback
Este comando da por concluida la transacción actual y deshace los cambios que se pudiesen
haber producido en la misma, liberando las filas bloqueadas. Se utiliza especialmente cuando
no se puede concluir una transacción porque se han levantado excepciones.
Savepoint
Se utiliza para poner marcas o puntos de salvaguarda al procesar transacciones. Se utiliza
junto con rollback permitiendo deshacer cambios hasta los savepoint.
El número de savepoint esta limitado a 5 por sesión pero lo podemos modificar con la
siguiente sentencia:
savepoint numero;
Rollback implicito
Este comando se ejecuta cuando un programa almacenado (procedimiento o función) falla y
no se controla la excepción que produjo el fallo. Pero si en el programa tenemos un commit
estos cambios no serán deshechos.
Rollback to
Deshace el trabajo realizado después del punto indicado. Pero no se confirma el trabajo
hecho hasta el savepoint. La transacción no finaliza hasta que se ejecuta un comando de
control de transacciones o hasta que finaliza la sesión.
Os dejo a continuación un ejemplo bastante completo de lo que sería el control de
transacciones:
create or replace procedure prueba (nfilas number)
as
begin
savepoint ninguna;
insert into tmp values ('primera fila');
savepoint una;
insert into tmp values ('segunda fila');
savepoint dos;
if nfilas=1 then
rollback to una;
else if nfilas=2 then
rollback to dos;
else
rollback to ninguna;
end if;
commit;
exception
when other then
rollback
end prueba;
Con este artículo terminamos la parte básica sobre oracle, PL/SQL y pasamos a lo que
podemos denominar programación avanzada de sql. Empezaremos con triggers en el
siguiente artículo.
En el presente artículo vamos a estudiar acerca de los triggers, donde veremos qué son y
cómo se construyen, comenzando con los trigger de tablas y más tarde veremos los trigger
de sustitución y los de sistema. Para ello lo primero que tenemos que ver es su definición.
Trigger a tablas
Un trigger es un bloque de código PL/SQL que se almacenan en la base de datos. Los bloques
de código de los triggers están asociados a una tabla y se ejecutan automáticamente cuando
se producen ciertos eventos asociados a la tabla.
Se suelen utilizar para prevenir transacciones erróneas y nos sirven también para
implementar restricciones de integridad o seguridad.
Su formato básico es el siguiente:
create or replace trigger nombre_trigger
{before | after} {delete | insert | update[of lista_columnas]}
[or {before | after} {delete|insert|update [of lista_columnas]}]
on nombre_tabla
[for each {row | statement | when (condición)}]
/* comienza el trigger */
[declare]
<declaraciones>
begin
<instrucciones>
[exception]
<excepciones>
end;
Elementos de un trigger
before / after: elemento que dispara el trigger
nombre: nombre del trigger que tiene que ser único.
for each: nivel del disparo del trigger que por defecto es statement que significa que se
dispara una sola vez por cada operación independientemente del número de filas afectadas.
for each row: salta por cada fila afectada.
Variables posibles para update: la primera es :old que hace referencia a los valores
anteriores y :new que hace referencia a los nuevos valores de actualización de la fila.
Tenemos que tener en cuanta unos cuantos aspectos:
Cuando el evento que dispara el trigger es un delete haremos referencia al valor :old
porque el valor :new es nulo
Cuando el evento que dispara el trigger es un insert haremos referencia al valor :new
porque el :old es nulo.
Cuando el evento es un update tiene sentido hacer referencia a los dos valores.
Sólo se pueden utilizar cuando el trigger es a nivel de fila (for each row).
Vamos a crear un trigger que se disparé automáticamente después de la modificación del
salario de la tabla empleado y pase un comentario a la tabla a auditar.
Create or replace trigger auditar_salario
after update of salario
on empleado
for each row
begin
insert into auditar values
('se ha modificado el salario' || :old.num_empleado);
end;
Orden de ejecución de los trigger
Una misma tabla puede tener varios triggers y el orden de disparo sería el siguiente:
1. Antes de comenzar a ejecutar la orden que provoca el disparo se ejecutaran los
triggers del tipo before.... for each statement
2. Para cada fila afectada por la orden:
a) se ejecutan los triggers del tipo before … for each row
b) se ejecuta la actualización de la fila
c) se ejecutan los triggers after... for each row
3. Una vez realizada la operación se ejecuta el after … for each statement
Cuanndo se dispara un trigger este forma parte de la operación que lo disparó de manera
que si el trigger falla, Oracle dará por fallida la operación completa. Aunque el fallo sea a
nivel de fila se hará rollback a toda la operación.
Ejemplo:
create or replace trigger nombre_trigger
before insert or delete
begin
if insert then
.....
elseif deleting then
....
elseif updating then
...
end if
....
end;
Disparadores de sustitución
Podemos crear triggers que no se ejecutan antes ni después de una instrucción sino en lugar
de (instead of).
Solo podemos utilizar estos triggers si están asociados a vistas, además actúan siempre a
nivel de fila.
La sintaxis de este tipo de trigger es la siguiente:
create [or replace] trigger nombre_trigger
instead of { insert | delete | update [of columnas]}
[ or { insert | delete | update}]
on nombre vista
[ for each row]
[declare]
declaraciones
begin
instrucciones
[execption]
excepciones
end;
Sobre una vista podemos hacer un select pero si hacemos un insert, delete o update puede
darnos problemas y no dejar ejecutarse la orden correctamente.
Los trigger sobre vistas vas a sustituir a estas operaciones por las correspondientes en las
tablas que forman la vista.
Un ejemplo de trigger de sustitución seria el siguiente:
create or replace trigger t_borrado_emp
instead of delete on empleado
for each row
begin
delete from emple where emp_num=:old.cod
end;
Disparadores de sistema
Estos trigger se disparan cuando se arranca o para la base de datos, entra o sale un usuario,
cuando se crea, modifica o elimina un objeto, etc.
En Oracle para crear este tipo de trigger tenemos que tener privilegios de Administer
database trigger.
La sintaxis de este trigger seria la siguiente:
create [or replace] trigger nombre_trigger
{ before | after } { <lista eventos de definición> | <lista eventos del sistema>}
on { database | schema} [when (condición)]
<cuerpo del trigger (bloque PL/SQL)>
Donde la lista de eventos de definición puede tener uno o más eventos DDL separados por or
y la lista de eventos del sistema igualmente separados por or.
Al asociar un disparador a un evento debemos indicar el momento en que se dispare. A
continuación os dejo una tabla de evento, momento y cuando se dispararía para dejarlo todo
mas o menos claro.
Evento Momento Se disparan:STARTUP AFTER Después de arrancar la instancia
SHUTDOWN BEFORE Antes de apagar la istancia
LOGON AFTERDespués de que el usuario se conecte a
la base de datos.LOGOFF BEFORE Antes de la desconexión de un usuario
SERVERERROR AFTER Cuando ocurre un error en el servidor
CREATEBEFORE |
AFTERAntes o después de crear un objeto en el
esquema
DROPBEFORE |
AFTERAntes o después de borrar un objeto en
el esquema
ALTERBEFORE |
AFTERAntes o después de cambiar un objeto
en el esquema
TRUNCATEBEFORE |
AFTERAntes o después de ejecutar un
comando truncate
GRANTBEFORE |
AFTERAntes o después de ejecutar un
comando grant
REVOKEBEFORE |
AFTERAntes o después de ejecutar un
comando revoke
DLLBEFORE |
AFTERAntes o después de ejecutar cualquier
comando de definición de datos
Oracle tiene algunas funciones que permiten acceder a los atributos del evento del disparo
ORA_YSEVENT, ORA_LOGIN, etc. Estas funciones pueden usarse en la clausula WHEN o en el
cuerpo del disparador. En el manual de Oracle podéis encontrar el listado de todas estas
funciones.
Un ejemplo seria un trigger que nos guarda los datos de un usuario al hacer login en la base
de datos:
create or replace trigger control
after logon
on database
begin
insert into control_conexion (usuario, momento, evento)
values {ORA_LOGIN_USER, SYSTIMESTAMP, ORA_SYSEVENT);
end;
Vamos a ver que son los paquetes en el sistema gestor de base de
datos Oracle, explicando estructura y funcionamiento.
Por Sara AlvarezAtención: Contenido exclusivo de DesarrolloWeb.com. No reproducir. Copyright.
En este artículo que pertenece al tutorial de Oracle trateremos el tema de los paquetes de
forma detenida.
Los paquetes en Oracle se utilizan para guardar subprogramas y otros objetos de la base de
datos.
Un paquete consta de los siguientes elementos:
Especificación o cabecera: contiene las declaraciones públicas (es decir, accesibles
desde cualquier parte de la aplicación) de sus programas, tipos, constantes,
variables, cursores, excepciones, etc.
Cuerpo: contiene los detalles de implementación y declaraciones privadas, es decir,
accesibles solamente desde los objetos del paquete.
La sintaxis de la cabecera es la siguiente:
create [or replace] package nombre_paquete as
<declaraciones públicas>
<especificaciones de subprogramas>
end nombre_paquete;
La sintaxis del cuerpo sería la siguiente:
create [or replace] package body nombre_paquete as
<declaraciones privadas>
<código de subprogramas>
[begin
<instrucciones iniciales>]
end nombre_paquete;
Como podéis observar la cabecera se compila independientemente del cuerpo. Os dejo un
ejemplo de paquete para que lo veáis más claro.
/* Cabecera */
create or replace package busar_emple as
TYPE t_reg_emple is RECORD
(num_empleado emple.emp_no%TYPE,
apellido emple.apellido%TYPE,
salario emple.salario%TYPE,
departamento emple.dept_no%TYPE);
procedure ver_por_numero(v_cod emple.emp_no%TYPE);
procedure ver_por_apellido(v_ape emple.apellido%TYPE);
function datos (v_cod emple.emp_no%TYPE)
return t_reg_emple;
end buscar_emple;
/* Cuerpo */
create or replace package body buscar_emple as
vg_emple t_reg_emple;
procedure ver_emple; /* procedimiento privado*/
procedure ver_por_numero (v_cod emple.emp_no%TYPE)
is
begin
select emp_no, apellido, salario, dept_no into vg_emple from emple where
emp_no=v_cod;
ver_emple;
end ver_por_numero;
procedure ver_por_apellido (v_ape emple.apellido%TYPE)
is
begin
select emp_no,apellido,salario,dept_no into vg_emple from emple where
apellido=v_apellido;
ver_emple;
end ver_por_apellido;
function datos (v_cod emple.emp_no%TYPE)
return t_reg_emple
is
begin
select emp_no,apellido,salario,dept_no into vg_emple from emple where
emp_no=v_cod;
procedure ver_emple
is
begin
DBMS_OUTPUT.PUT_LINE(vg_emple.num_empleado || '*' || vg_emple.apellido || '*'
|| vg_emple.salario || '*'|| vg_emple.departamento);
end ver_emple;
end buscar_emple;
Como podéis ver este paquete nos permite buscar un empleado de tres formas distintas y
visualizar sus datos.
Utilización de los objetos definidos en los paquetes
Podemos utilizar los objetos definidos en los paquetes básicamente de dos maneras distintas:
Desde el mismo paquete: esto quiere decir que cualquier objeto puede ser utilizado
dentro del paquete por otro objeto declarado en el mismo.
Para utilizar un objeto dentro del paquete tan sólo tendríamos que llamarlo. La
llamada sería algo así: v_emple :=buscar_emple.datos(v_n_ape); (como veis no
utilizamos el execute ya que nos encontramos dentro del paquete).
Desde fuera del paquete: Podemos utilizar los objetos de un paquete siempre y
cuando haya sido declarado en la especificación del mismo. Para llamar a un objeto o
procedimiento desde fuera del paquete utilizaríamos la siguiente notación: execute
nombre_paquete.nombre_procedimiento(lista de parametros);
Declaración de cursores en paquetes
En los paquetes también podemos introducir cursores, para ello debemos declararlo en la
cabecera del paquete indicando su nombre, los parámetros y tipo devuelto. Para que lo veáis
más claro os dejo un ejemplo a continuación:
CREATE or REPLACE PACKAGE empleados AS
.....
CURSOR a RETURN empleado%ROWTYPE;
...
END empleados;
CREATE or REPLACE PACKAGE BODY empleados AS
....
CURSOR a RETURN empleado%ROWTYPE
SELECT * FROM empleado WHERE salario < 10000;
....
END empleados;
Los paquetes suministrados por Oracle son:
Standard : tiene la función to_char y abs
dbms_standard: tiene la función raise_aplication_error
dbms_output: con la función put_line
dbms_sql: que utiliza sql de forma dinámica.
NOTA: sql dinámico significa que el programa es capaz de ejecutar órdenes de definición y manipulación
sobre objetos que sólo se conocen al ejecutar el paquete.
Un ejemplo de la utilización de dbms_sql es el siguiente:
BEGIN
......
id_cursor := DBMS_SQL.OPEN_CURSOR;
DMBS_SQL.PARSE(id_cursor, instrucción,DMBS_SQL.V3);
v_dum :=DMBS_SQL.EXECUTE(id_cursor);
DMBS_SQL.CLOSE_CURSOR(id_cursor);
......
Lo que hacemos es abrir el cursor y nos devuelve el id del mismo para poder trabajar con él.
Después tenemos el DMBS_SQL.PARSE que analiza la instrucción que se va a ejecutar. Ya en
la siguiente linea ejecutamos la sentencia y por último cerramos el cursor.
No os preocupéis si no habéis terminado de entenderlo ya que dedicaremos un articulo
completo a la utilización de DBSM_SQL.
Top Related