You are on page 1of 16

Curso de PL/SQL

3. VARIABLES

Ya hemos visto algunas variables utilizadas en el capítulo anterior,


además de algunos tipos de datos. Con PL/SQL se pueden declarar variables
y luego utilizarlas en nuestro código. Vamos como en cualquier lenguaje,
jejeje.

• Declararemos las variables en la sección de declaración.


• Asignaremos valores a las variables dentro de la sección de código.
• Pasaremos valores a los bloques PL/SQL mediante parámetros.
• Recogeremos resultados en variables de salida.

La declaración de variables ya la hemos visto antes, aunque no con


todas sus opciones. Especificamos el nombre de la variable, el tipo y
opcionalmente un valor inicial y una restricción NOT NULL e incluso un
valor por defecto.

DECLARE
nombre_variable tipo_variable [NOT NULL] [:= expr | DEFAULT expr] ;

Ejemplos:

DECLARE
nombre_empleado VARCHAR2(30);
contador NUMBER(6) NOT NULL := 0 ;
porcentaje NUMBER(3) DEFAULT 16 ;
fecha_alta DATE DEFAULT sysdate ;
correcto BOOLEAN NOT NULL := TRUE ;

NOT NULL : Es opcional y restringe la variable para que tenga que contener
un valor no nulo. Las variables NOT NULL deben ser inicializadas
obligatoriamente.

:= expr : El símbolo de asignación, como ya hemos visto es el := y expr es


cualquier expresión PL/SQL que puede ser un valor, otra variable o una
expresión que contenga operadores y funciones.

DEFAULT expr : Valor por defecto de la variable. expr es cualquier


expresión PL/SQL que puede ser un valor, otra variable o una expresión que
contenga operadores y funciones.

También podemos declarar una constante, prohibiendo así que se


cambie su valor.

c_comision CONSTANT NUMBER(5,2) := 10.50 ;

En las declaraciones de constantes. La palabra clave CONSTANT debe


preceder al tipo de dato, y debe ser inicializada en la declaración.

REGLAS PARA NOMBRES DE VARIABLES

Dos variables pueden tener el mismo nombre. Sí, habéis leído bien.
Siempre y cuando estén definidas en distintos bloques. Si se trata de
bloques anidados, la vigente es la definida en el bloque actual.

No se deben poner nombres de variables que coincidan con nombres de


campos (columnas) de las tablas con las que se va a trabajar. Esto es así

Pág 1 de 16
Curso de PL/SQL

porque si se da la coincidencia, prevalece el nombre del campo de la


tabla, es decir, la variable sería “invisible” para el bloque en cuestión
(excepto en alguna circunstancia). No es buena práctica, desde luego.

Los nombres de variables no deben pasar de 30 caracteres. El primero


debe ser una letra y los demás pueden ser letras, números o símbolos
especiales.

Los literales de cadenas deben ir entre comillas simples ('Hola


mundo'). Si necesitas incluir una comilla simple en tu cadena, solo tienes
ponerlo dos veces ('Hola ''amigo'' mio --> Hola 'amigo' mio).

3.1 TIPOS DE VARIABLES

Todas las variables PL/SQL tienen un tipo de dato que especifica un


formato de almacenamiento, restricciones y un rango válido de valores. Los
tipos de variables en PL/SQL son variados:

• Escalares: Contienen un valor único.


• Compuestos: Permiten que se definan y manipulen grupos de campos en
bloques PL/SQL. El típico es el registro (record).
• Referenciados: Son los llamados punteros. No los veremos en este curso.
• LOB (Large Objects): Contienen valores llamados localizadores que
especifican la ubicación de lobs como imágenes y que están almacenados
fuera de la base de datos. Tampoco los veremos en este curso.

Los principales tipos de variables escalares son:

• VARCHAR2(longitud_máxima): Caracteres de longitud variable de hasta


32767 bytes (en Oracle 8i) No hay tamaño por defecto. longitud_máxima
especifica exactamente eso: la longitud para esa variable.
• NUMBER(posiciones, decimales): Números de coma flotante y fija. El
tamaño total de la variable viene dado por posiciones (incluyen los
decimales pero no el punto decimal) y los decimales vienen especificados
por decimales. Ejemplo: porcentaje NUMBER (5,2) --> 5 posiciones en
total, de las cuales 2 son decimales --> 100.00, 56.45, 5.9, etc...
• DATE: Fecha y hora. Los valores DATE incluyen la hora del día en
segundos desde la medianoche. El rango de fechas está entre 4712 BC y
9999 AC.
• CHAR(longitud_máxima): Caracteres de longitud fija. Si no se especifica
longitud_máxima, su longitud será 1. Se suelen utilizar los VARCHAR2 ya
que ahorran espacio de almacenamiento y de memoria.
• LONG: Caracteres de longitud variable. Hasta 32K en la variable, pero
hasta 2Gb en un campo de la base de datos (he ahí la diferencia con
VARCHAR2)
• LONG RAW: Datos binarios o cadenas de bytes de 32760 bytes. No se suelen
usar ya que PL/SQL NO los interpreta pero la Base de datos los admite.
• BOOLEAN: Datos lógicos. Tres valores posibles (sí, TRES): TRUE, FALSE y
NULL.
• BINARY_INTEGER: Enteros entre -2147483647 y +2147483647. Se suelen
utilizar para los índices de los ARRAY's (llamados “tablas” en PL/SQL.
Sí ya se que lía un poco, pero es acostumbrarse, ya lo veréis).

Los tipos de datos compuestos los veremos en el punto 3.3, veamos


ahora algo muy interesante y ligado a la declaración de variables.

Pág 2 de 16
Curso de PL/SQL

3.2 DECLARACION DE VARIABLES CON EL ATRIBUTO %TYPE

Ya vimos en los ejemplos anteriores como declarábamos variables en


los bloques de código. Ahora hemos visto con detalle los tipos de
variables de las que disponemos. En los ejemplos anteriores declarábamos
una variable para almacenar el nombre de un campo una vez recuperado de la
tabla correspondiente. Era el caso del nombre del empleado.

Supongamos que llega un momento en que nos damos cuenta que la


longitud inicial del campo se nos ha quedado pequeña. Bien, pues
modificamos la definición del campo en las tablas en las que aparezca y
arreglado. Pues de arreglado nada. ¿Qué pasa con todo el código escrito en
PL/SQL con variables que hagan referencia a ese campo?. También tenemos
que modificarlo y eso sí que supone mucho trabajo. En un entorno de
producción imaginaros el caos que se puede formar.

Bien, no pasa nada. Todo es cuestión de buenas costumbres. Esto ya


está previsto y se soluciona con el atributo %TYPE usado en la declaración
de variables que hagan referencia a un campo de una tabla.

DECLARE
v_nombre EMPLEADOS.nombre%TYPE ;

De esta forma, estamos declarando la variable v_nombre de tal forma


que sea del mismo tipo, longitud, etc... que el campo nombre de la tabla
EMPLEADOS. Si cambiamos la definición de dicho campo en la tabla, los
programas, procedimientos, funciones, paquetes, triggers, etc...
automáticamente registrarán el cambio en tiempo de ejecución ya que lo que
se guardó al compilarlo es “eres del tipo que tenga el campo tal de la
tabla cual”. Ni siquiera hay que volverlos a compilar. ¿A que es una buena
costumbre usar el atributo %TYPE?.

Realmente no solo sirve para variables que hagan referencia a campos


de tablas, también puede usarse con otras variables, pero se utiliza
mayoritariamente cuando el valor almacenado en la variable se derivará de
una tabla de la la base de datos. Si queremos usarla referenciando a otra
variable:

DECLARE
v_balance NUMBER(7,2) ;
v_balance2 v_balance%TYPE := 10 ;

También útil, pero parece que no tanto, ¿verdad?. Bueno es mi


opinión.

3.3 TIPOS DE DATOS COMPUESTOS

Los tipos de datos compuestos son también llamados colecciones,


aunque a mi no me gusta el término. Son REGISTROS y TABLAS (alguno más hay
pero veremos estos). Os recuerdo de nuevo: TABLA se refiere a ARRAY.

3.3.1 REGISTROS

Un registro es un grupo de elementos de datos almacenados en campos,


cada uno con su propio nombre y tipo de datos (vamos, como el registro de
una base de datos).

Pág 3 de 16
Curso de PL/SQL

Por ejemplo, supongamos un proceso en el que vamos a necesitar


acceder a los datos de empleado siguientes: nombre y departamento. Podemos
estructurar un tipo de datos registro para tener todos esos campos dentro
de una unidad lógica. (Estoy seguro que el tipo de datos registro os
sonará de otros lenguajes).

Características de los registros en PL/SQL

• Cada registro definido puede tener tantos campos como sea necesario.
• Se les pueden asignar valores iniciales y pueden definirse como NOT
NULL.
• Los campos sin valores iniciales se rellenan con NULL.
• Podemos usar también la palabra clave DEFAULT.
• Podemos definir el tipo registro (RECORD) y declarar variables de de ese
tipo dentro de la parte de declaraciones de cualquier bloque,
subprograma ó paquete.
• Un registro puede formar parte de otro registro.

Para crear un tipo registro se hace así:

TYPE nom_tipo IS RECORD (declaracion_campo1[,declaracion_campo2, ...]) ;

donde declaracion_campo1 significa:

nombre_campo {tipo_campo | variable%TYPE | tabla.columna%TYPE |


tabla%ROWTYPE} [[NOT NULL] {:= | DEFAULT} expr ]

Parece complicado, pero no lo es. Veamos como siempre un ejemplo y


se nos aclarará la vista:

DECLARE
TYPE tipo_reg_empleado IS RECORD (nom EMPLEADOS.nombre%TYPE,
dep EMPLEADOS.departamento%TYPE) ;

registro_empleado tipo_reg_empleado ;

La primera linea es la definición del tipo “tipo_reg_empleado” que


es un RECORD con dos campos que son del mismo tipo que los campos nombre y
departamento de la tabla EMPLEADOS. He utilizado nombres distintos por
claridad, no por otra cosa, ya que podría haber usado los mismos que en la
tabla pues estos están dentro del registro y los podré identificar
perfectamente. ¿Cómo?, pues muy sencillo. Para referenciar un campo de un
registro se utiliza la notación:

nombre_registro.nombre_campo

Por ejemplo:

DECLARE
TYPE tipo_reg_empleado IS RECORD (nom EMPLEADOS.nombre%TYPE,
dep EMPLEADOS.departamento%TYPE) ;

registro_empleado tipo_reg_empleado ;

Pág 4 de 16
Curso de PL/SQL

BEGIN
....
....
registro_empleado.nom := 'Pepe' ;
registro_empleado.dep := 10;
....
....
END;

Más de uno se habrá fijado en el %ROWTYPE que he escrito arriba en


la explicación del contenido de declaracion_campo1. Pues es lo que parece.
Podemos definir un registro directamente como un registro de la tabla
indicada antes del %ROWTYPE, con lo que si queremos manejar el registro
completo de una tabla, estaremos ahorrando unas cuantas pulsaciones a la
hora de escribir:

DECLARE
TYPE tipo_reg_empleado IS RECORD (fila EMPLEADOS%ROWTYPE) ;

reg_empleado tipo_reg_empleado ;

Y aquí referenciaríamos como reg_empleado.fila

Claro, ¿y como accedemos a los campos de fila? Pues ese es el


problema, que esto es parte de la definción “formal”. Es mucho más
sencillo declarar un registro con el atributo %ROWTYPE de la siguiente
forma:

DECLARE
reg_empleado EMPLEADOS%ROWTYPE ;

Como veís, hemos abreviado mucho más. De golpe hemos definido un


registro y declarado la variable. Ahora, referenciar los campos del
registro es como ya hemos visto antes:

nombre_registro.nombre_campo

Este método tiene la misma ventaja que el atributo %TYPE para las
variables. Es decir: si cambiamos la estructura de la tabla referenciada,
añadiéndole o quitándole campos, las definiciones de variables quedan
inalterables. Lo malo es que en el código sí tendremos que “retocar” esos
cambios, ya que deberemos referenciar los nuevos campos ó eliminar las
referencias a los campos eliminados. Pero algo es algo ¿no?.

Además tiene las siguientes ventajas:

• El número y los tipos de datos de las columnas de la base de datos


pueden no ser conocidos y con %ROWTYPE no hace falta conocerlos.
• El número y los tipos de datos de las columnas de la base de datos puede
cambiar en el momento de ejecución (ya comentado)
• Muy útil para recuperar una fila con la sentencia SELECT. Ejemplo:

Pág 5 de 16
Curso de PL/SQL

DECLARE
reg_empleado EMPLEADOS%ROWTYPE ;
BEGIN
SELECT * INTO reg_empleados FROM EMPLEADOS ;
....
....
END;

3.3.2 TABLAS PL/SQL

Los objetos de tipo TABLE se llaman tablas PL/SQL. Están


estructurados como tablas de la base de datos (pero NO son lo mismo).
Utilizan una clave primaria para ofrecer acceso de tipo vector a las filas
(ahora ya sabéis porque las llamo ARRAY's).

Una tabla PL/SQL es similar a un vector y debe tener dos


componentes:

• Una clave primaria del tipo de datos BINARY_INTEGER que indexa la tabla
PL/SQL
• Una columna de un tipo de datos escalar o de registro, que almacena los
elementos de la tabla PL/SQL.

Las tablas PL/SQL pueden aumentar dinámicamente ya que no hay


restricción al respecto, es decir, el tamaño de una tabla PL/SQL aumenta a
medida que le vamos añadiendo filas. A los componentes de la tabla PL/SQL
(siento repetir constantemente “tabla PL/SQL” pero no quiero que se
confunda con la tabla de base de datos) no se les puede poner nombre.

Vamos a crear el tipo y declarar una tabla PL/SQL:

DECLARE
TYPE tipo_tabla_fechas IS TABLE OF DATE INDEX BY BINARY_INTEGER ;
tabla_fechas tipo_tabla_fechas ;

Y ahora la sintaxis “formal”:

TYPE nombre_tipo IS TABLE OF {tipo_campo | variable%TYPE |


tabla.campo%TYPE} [NOT NULL]
INDEX BY BINARY_INTEGER ;

El acceso a los elementos de la tabla es muy sencillo. Accederemos


por la clave primaria que es el BINARY_INTEGER, osea, un numero entero.

DECLARE
TYPE tipo_tabla_fechas IS TABLE OF DATE INDEX BY BINARY_INTEGER ;
tabla_fechas tipo_tabla_fechas ;
BEGIN
tabla_fechas(1) := sysdate ;
tabla_fechas(2) := TO_DATE('01/01/2004','DD/MM/RRRR') ;
....
....
END ;

Pág 6 de 16
Curso de PL/SQL

Recordar que los valores para BINARY_INTEGER estaban comprendidos


entre -2147483647 y +2147483647, luego el valor de la clave primaria puede
ser perfectamente negativo, no tiene porque empezar con 1 (aunque suela
ser lo más normal).

Ahora veamos una tabla PL/SQL de registros:

DECLARE
TYPE tipo_tabla_reg_dep IS TABLE OF DEPARTAMENTOS%ROWTYPE
INDEX BY BINARY_INTEGER ;
tabla_dep tipo_tabla_reg_dep ;

En este ejemplo podemos hacer referencia a los campos de los


registros de la tabla PL/SQL tabla_dept perfectamente ya que todos sus
elementos son registros. ¿Y como lo hago? Pues sencillo:

tabla_dep(indice).campo

Es decir, podríamos cargar registros de una tabla de base de datos a


una tabla PL/SQL e ir recorriéndolos como si tal cosa con la única
limitación de la memoria que tengamos, ya que las tablas PL/SQL van
ocupando memoria física a medida que se van llenando.

Ahora mismo veremos que esto no es una práctica habitual, ya que


para eso tenemos los cursores, los cuales nos resolverán la mayoría de
nuestros problemas a la hora de programar código en el que las tablas (de
base de datos) sean las protagonistas.

4. CURSORES

Se de uno que, llegados a este punto, estará ávido de leer, jejeje.


Después de hacerlo el objetivo es saber distinguir entre un cursor
explícito y uno implícito y utilizar una variable y un bucle de cursor FOR
para recorrerlo. Alguna cosita más vamos a ver, ya veréis.

Cuando necesitamos una sentencia SELECT que devuelva varias filas


para procesarlas, es que necesitamos un cursor.

Ya dije en anteriores capítulos que una sentencia SELECT en el


código PL/SQL ejecutable sólo puede devolver una fila. Es más, en esos
casos se utiliza siempre combinado con la palabra clave INTO y si nos
devuelve más de una fila, se produce la excepción TOO_MANY_ROWS (otra
excepción a anotar para tener en cuenta a la hora de capturar errores).

Pues bien, ahora queremos seleccionar (SELECT) una cantidad


indeterminada de filas de una o varias tablas (mediante sus joins
correspondientes – Revisa el Curso de SQL si te hace falta) y recorrerlas
para procesarlas, realizar cálculos, efectuar transacciones, etc.... Eso
es un cursor. Además es un cursor explícito, ya que lo vamos a definir
nosotros y lo vamos a manejar a nuestro antojo. Entonces.... ¿qué es un
cursor implícito?. Pues es el cursor que se asocia a TODA sentencia DML y
PL/SQL SELECT ejecutada. Me explico un poco más: PL/SQL declara
implícitamente cursores para todas las sentencias DML (Data Manipulation
Language) y PL/SQL SELECT incluyendo consultas que solo devuelven una
fila. Vamos, que hemos estado usando cursores desde el principio ;-)

Pág 7 de 16
Curso de PL/SQL

4.1 CURSORES IMPLICITOS

Ejemplo: Efectuamos desde linea de comandos una actualización masiva


de un campo de una tabla. Imaginemos que a todos los nombres de empleado
les vamos a añadir 'Don ' por delante del nombre.

UPDATE EMPLEADOS SET nombre='Don '||nombre ;

Que no se diga: Toda la tabla actualizada. Bien, pues esta sentencia


DML ha provocado que se declare un cursor implícito que recorra las filas
que se van a actualizar (en este caso todas) y las actualice una a una.
¿Hemos hecho algo nosotros?. No. Simplemente realizar una actualización.
¿Tiene nombre ese cursor? Sí. PL/SQL nos deja referirnos a él como “SQL”,
pero cuidado: solo tenemos accesible el último ya que usa el mismo nombre
para todos los cursores implícitos. Además no podemos hacer mucho con él,
tan solo podemos usar los atributos de cursor para obtener información de
lo que ha ocurrido, insisto, en el último cursor implícito.

4.1.1 ATRIBUTOS DE CURSORES IMPLICITOS

Tenemos cuatro atributos para los cursores implícitos que nos


permiten evaluar lo que pasó cuando se utilizó por última vez el cursor
implícito “SQL”. Estos atributos se utilizan como funciones y no se pueden
usar en sentencias SQL. Sin embargo sí que las podemos evaluar en
expresiones de comparación, etc...

SQL%ROWCOUNT Número de filas afectadas (es un valor entero)


SQL%FOUND Atributo booleano que da como resultado TRUE si la
sentencia SQL mas reciente afecta a una ó más filas.
SQL%NOTFOUND Atributo booleano que da como resultado TRUE si la
sentencia SQL mas reciente no afecta a ninguna fila.
SQL%ISOPEN Siempre resulta FALSE porque PL/SQL cierra los cursores
implícitos una vez ejecutados.

Estos atributos pueden ser utilizados en la sección de excepciones


de un bloque para reunir información sobre la ejecución de una sentencia
DML. Aquí es importante recordar que PL/SQL NO considera una excepción una
sentencia DML que no afecte a ninguna fila, al contrario de lo que ocurre
con una SELECT que devuelve una excepción (recordemos: NO_DATA_FOUND)

4.2 CURSORES EXPLICITOS

Los cursores explícitos se utilizan para procesar individualmente


las filas devueltas por una sentencias SELECT que puede devolver varias
filas. El número de filas viene determinado por los criterios de búsqueda
especificados en la claúsula WHERE de la sentencia SELECT. Un cursor
explícito se declara, se abre, se procesan las filas devueltas y se cierra
para finalizar. La “historia” es ir fila a fila evaluándola y realizando
las acciones deseadas para cada una de ellas. Para eso usamos los bucles
de cursor, y además tenemos varios tipos.

Pág 8 de 16
Curso de PL/SQL

El esquema básico sería este:

SI
DECLARE ---> OPEN ---> FETCH ---> ¿VACIO? -------> CLOSE
^ |
|___________| NO

1. Declaramos el cursor nombrándolo y definiendo la estructura de la


consulta que se va a ejecutar.
2. Abrimos el cursor. La sentencia OPEN ejecuta la consulta y prepara las
filas para ser recuperadas una a una.
3. Recuperamos una fila del cursor. La sentencia FETCH toma la fila actual
y la carga en variables. Cada recuperación hace que el cursor mueva su
puntero hacia la siguiente fila del conjunto total de filas. Por tanto
cada vez que hacemos un FETCH recuperamos una fila distinta.
4. Cerramos el cursor. La sentencia CLOSE libera las filas.

Vamos a ver como lo declaramos:

DECLARE
v_codigo EMPLEADOS.codigo%TYPE ;
v_nombre EMPLEADOS.nombre%TYPE ;

CURSOR c_empleados IS SELECT codigo, nombre


FROM EMPLEADOS
WHERE departamento=3 ;

Con OPEN c_empleados ; lo abriríamos.

Con FETCH c_empleados INTO v_codigo, v_nombre ; recuperamos una fila


y la introducimos en las variables a tal efecto declaradas. Podríamos
haber definido un tipo RECORD y una variable de ese tipo para almacenar la
fila completa (ejercicio para el lector) y quizás fuera más elegante.

Con CLOSE c_empleados ; lo cerraríamos.

Nos faltaría recorrerlo mediante un bucle LOOP – EXIT WHEN o un


bucle FOR que son los que yo suelo utilizar (manías mías ó quizás
comodidad - ya veréis por qué cuando veamos bucles de cursor-, todos los
bucles sirven y con todos se puede hacer todo).

Supongamos que se recuperan estas filas:

1 Pepito Boss <---- puntero actual


2 Manolito Programmer
25 Juanito Administrator
4 Luisito Perez

Con el primer FETCH, el puntero se sitúa en la segunda fila y hemos


recuperado la primera fila.

1 Pepito Boss
2 Manolito Programmer <---- puntero actual
25 Juanito Administrator
4 Luisito Perez

Pág 9 de 16
Curso de PL/SQL

Así hasta llegar al final del juego de resultados. Y aquí es donde


entran los atributos de cursor que antes hemos nombrado. ¿Cómo se cuando
he llegado al final? ¿Cómo se si el cursor está abierto o cerrado? ¿Como
se cuantas filas he recuperado con el cursor sin tener que contarlas una a
una?. La respuesta son los atributos de cursor.

4.2.1 ATRIBUTOS DE CURSORES EXPLICITOS

Adivinad: Los mismos ;-) Pero con una diferencia en %ISOPEN

%ROWCOUNT Número de filas afectadas (es un valor entero)


%FOUND Atributo booleano que da como resultado TRUE si la
sentencia SQL mas reciente afecta a una ó más filas.
%NOTFOUND Atributo booleano que da como resultado TRUE si la
sentencia SQL mas reciente no afecta a ninguna fila.
%ISOPEN Atributo booleano. Resulta TRUE si el cursor todavía
está abierto (aquí el cursor lo abrimos y cerramos nosotros).

El atributo %NOTFOUND nos servirá para llevar el control en la


recuperación de filas, ya que cuando lleguemos a la última mediante un
FETCH, este atributo valdrá TRUE ya que el puntero ya estaba al final del
grupo de filas y la siguiente recuperación falla.

El atributo %ISOPEN lo utilizamos para comprobar que el cursor esté


abierto antes de comenzar a recuperar filas. Por ejemplo así:

IF NOT c_empleados%ISOPEN THEN


OPEN c_empleados ;
END IF ;

El atributo %ROWCOUNT también sirve para controlar la recuperación


de datos ya que se va incrementando a medida que hacemos un FETCH y podría
servirnos para recuperar n filas (combinándolo con %NOTFOUND). Por
ejemplo, estas lineas dentro de un bucle LOOP (ya lo veremos) recuperaria
5 filas o menos si las filas recuperadas son menos de 5.

FETCH c_empleados INTO v_codigo, v_nombre ;


EXIT WHEN c_empleados%ROWCOUNT > 5 or c_empleados%NOTFOUND ;

NOTA: Antes de la primera recuperación %NOTFOUND vale NULL.

Por último, ¿ya has hecho el ejercicio de definir un cursor con un


RECORD?, ¿no? Pues ahí va:

DECLARE
CURSOR c_empleados IS SELECT codigo, nombre
FROM EMPLEADOS
WHERE departamento=3 ;

reg_empleado c_empleados%ROWTYPE ;
BEGIN
OPEN c_empleados ;
..
FETCH c_empleados INTO reg_empleado ;
..
END;

Pág 10 de 16
Curso de PL/SQL

5. ESTRUCTURAS DE CONTROL

Las estructuras de control nos van a permitir tratar el control


condicional en los bloques PL/SQL utilizando sentencias IF y bucles. Estoy
seguro de que esta parte os será de fácil comprensión ya que no se
diferencia casi nada de las sentencias análogas de otros lenguajes de
programación.

5.1 CONTROL DE FLUJO. IF.

Una de las formas para cambiar el flujo lógico de sentencias dentro


del bloque PL/SQL es mediante las sentencias condicionales.

Hay tres formas de sentencias IF:

IF – THEN
IF – THEN – ELSE
IF – THEN – ELSIF

La sintaxis es:

IF condicion THEN
sentencias ;
[ELSIF condicion THEN
sentencias ;]
[ELSE
sentencias ;
END IF] ;

Ejemplo de IF simple: Pongamos en la variable v_jefe el valor 1 y en


la variable v_sueldo el valor 1500 si la variable v_nombre vale 'Luisito
Perez'.

IF v_nombre = 'Luisito Perez' THEN


v_jefe := 1 ;
v_sueldo := 1500 ;
END IF;

Ejemplo de IF – ELSE: Si la variable v_departamento es 10,


actualizamos la variable v_jefe a 1 y la variable v_sueldo a 2000. En caso
contrario, actualizamos solo la variable v_sueldo a 1000.

IF v_departamento = 10 THEN
v_jefe := 1 ;
v_sueldo := 2000 ;
ELSE
v_sueldo := 1000 ;
END IF;

Ejemplo de IF – ELSIF - ELSE: Si la variable v_departamento es 10,


actualizamos la variable v_jefe a 1 y la variable v_sueldo a 2000. Si
v_departamento es 20, actualizamos la variable v_jefe a 2 y la variable
v_sueldo a 1500. En otro caso, actualizamos solo la variable v_sueldo a
1000.

Pág 11 de 16
Curso de PL/SQL

IF v_departamento = 10 THEN
v_jefe := 1 ;
v_sueldo := 2000 ;
ELSIF v_departamento = 20 THEN
v_jefe := 2 ;
v_sueldo := 1500 ;
ELSE
v_sueldo := 1000 ;
END IF;

Si la condición resulta FALSE o NULL no se ejecutan las sentencias


asociadas. Se permite cualquier número de clausulas ELSIF, pero como
máximo puede haber un clausula ELSE.

Al escribir el código, recordemos siempre que ELSIF es una sola


palabra y que END IF son dos palabras.

Recordemos también que podemos manejar los valores nulos con el


operador IS NULL, y que las expresiones que tienen un operador NULL
resultan NULL.

5.2 CONTROL ITERATIVO. BUCLES LOOP, FOR, WHILE

PL/SQL proporciona varias maneras de estructurar bucles y así poder


repetir varias veces una sentencia o una secuencia de sentencias. Tenemos:

• Bucles básicos: Que proporcionan acciones repetitivas sin condiciones


globales.
• Bucles FOR: Para ofrecer un control iterativo de acciones basándose en
un recuento.
• Bucles WHILE: Para ofrecer control iterativo de acciones basándose en
una condición.
• Sentencia EXIT para terminar (salir de) los bucles.

Bucle básico: La forma más simple. Utilizaremos el LOOP – END LOOP. Sin
una sentencia EXIT, el bucle sería infinito.

Sintaxis:
LOOP
sentencia1 ;
sentencia2 ;
...
...
EXIT [WHEN condicion] ;
END LOOP ;

Siempre se entra en el bucle ya que la condición se evalúa dentro


del mismo. Si se termina el bucle por la sentencia EXIT, se salta a la
sentencia siguiente al END LOOP. Podemos escribir el EXIT como una acción
dentro de un IF o como una sentencia autónoma dentro del bucle, y en este
caso se le puede añadir la clausula WHEN condicion para permitir la
finalización condicional del bucle. Un bucle básico puede contener muchas
sentencias EXIT.

Pág 12 de 16
Curso de PL/SQL

Ejemplo: Vamos a insertar 10 registros de departamentos.

DECLARE
v_contador NUMBER(2):= 1 ;
BEGIN
LOOP
INSERT INTO DEPARTAMENTOS
VALUES(v_contador,'Departamento '||TO_CHAR(v_contador));
v_contador := v_contador + 1 ;
EXIT WHEN v_contador > 10 ;
END LOOP ;
END;

En este ejemplo usamos una variable como contador de registros


insertados en la tabla y la utilizamos como condición de salida del bucle.

Bucle FOR: Los bucles FOR tienen la misma estructura general que el bucle
básico. Además tiene una sentencia de control delante de la palabra clave
LOOP para determinar el número de repeticiones.

Sintaxis:
FOR indice IN [REVERSE] n..m LOOP
sentencia1 ;
sentencia2 ;
...
...
END LOOP ;

indice: Es un integer declarado implícitamente cuyo valor aumenta o


disminuye automáticamente (disminuye si se usa REVERSE) en 1 en cada
iteración del bucle hasta que se llega al límite superior ó inferior.

REVERSE: Hace que el índice disminuya con cada iteración desde el límite
superior al límite inferior.

n: Límite inferior

m: Límite superior

No hay que declarar el índice en ningún sitio. Se declara


implícitamente como ya he comentado, y solo existe dentro del mismo.
Tampoco se puede alterar su contenido mediante una asignación. Si el
límite inferior resulta ser superior al límite superior, el bucle NO se
ejecuta, pero no ocurre ningún error. Como siempre, un ejemplo:

DECLARE
n NUMBER := 1 ; m NUMBER := 100 ;
suma NUMBER(5) := 0 ;
suma2 NUMBER(5) := 0 ;
BEGIN
FOR i IN n..m LOOP
suma := suma + i ;
END LOOP ;
FOR i IN REVERSE n..m LOOP
suma2 := suma2 + i ;
END LOOP ;
END;

Pág 13 de 16
Curso de PL/SQL

Bucle WHILE: Es el bucle más generalizado en los lenguajes de programación


estructurados, por lo menos esa es mi opinión. Podemos utilizar el bucle
WHILE para repetir una secuencia de sentencias mientras la condición del
WHILE es TRUE. Se trata de un bucle “pre-condición”, es decir, la
condición se evalúa antes de entrar al bucle. En el bucle básico esto no
ocurría y en el bucle FOR sí, aunque es un poco “mezcla” de los dos.

Sintaxis:
WHILE condicion LOOP
sentencia1;
sentencia2;
....
....
END LOOP ;

condicion: Es una expresión o variable booleana (TRUE, FALSE ó NULL)

IMPORTANTE: Si las variables incluidas en condicion no cambian durante el


cuerpo del bucle, la condición seguirá siendo TRUE y el bucle será
infinito.

Ejemplo:

DECLARE
i NUMBER := 100 ;
j NUMBER := 1 ;
BEGIN
WHILE i<>j LOOP
i := i - 1 ;
j := j + 1 ;
END LOOP;
END ;

Para finalizar con los bucles, decir que se pueden anidar sin ningún
problema y de distinto tipo, claro. Nombraré de pasada que los bucles se
pueden “etiquetar” para hacer un EXIT etiqueta en caso de que tengamos
bucles anidados, pero no es algo que me guste ya que se puede implementar
mediante código estructurado. Si a alguien le interesa me puede preguntar
o ya sabéis: http://www.google.com

6. MANEJANDO CURSORES. BUCLES DE CURSOR

Ya hemos visto en el capítulo de cursores como declararlo, abrirlo,


ejecutar la consulta del cursor y cómo cerrarlo. Bien. Ahora sólo se trata
de aplicar un bucle a lo ya aprendido y podremos recorrer un cursor de
arriba a abajo.

Recordemos el esquema que pusimos:


SI
DECLARE ---> OPEN ---> FETCH ---> ¿VACIO? -------> CLOSE
^ |
|___________| NO

¿ A que se ve un bucle ahí bastante claro ? ;-)

Pág 14 de 16
Curso de PL/SQL

Pues casi que está escrito:

DECLARE
CURSOR c_empleados IS SELECT codigo, nombre
FROM EMPLEADOS
WHERE departamento=3 ;

reg_empleado c_empleados%ROWTYPE ;
BEGIN
--- Si no está abierto el cursor, lo abrimos.
--- Esto se hace por precaución.
IF NOT c_empleados%ISOPEN THEN
OPEN c_empleados ;
END IF;
--- Bucle básico
LOOP
-- Recuperamos datos y adelantamos el cursor.
FETCH c_empleados INTO reg_empleado ;

... sentencias para tratar los datos

-- Salimos cuando el puntero haya pasado del ultimo registro.


-- o porque no habían registros en el resultado.
EXIT WHEN c_empleados%NOTFOUND OR c_empleados%NOTFOUND IS NULL;
END LOOP;

-- Si el cursor está abierto, lo cerramos


IF c_empleados%ISOPEN THEN
CLOSE c_empleados ;
END IF;
END;

Muy bien, objetivo conseguido. Pero cuanto código para recorrer un


cursor que, al fin y al cabo es un conjunto de filas sacadas de una(s)
tabla(s). ¿Habrá una forma más sencilla?..... Por supuesto: Con un bucle
FOR de cursor.

Haced el favor de mirar este código:

DECLARE
CURSOR c_empleados IS SELECT codigo, nombre
FROM EMPLEADOS
WHERE departamento=3 ;
BEGIN
--- Bucle FOR de cursor
FOR fila IN c_empleados LOOP
... sentencias para tratar los datos
END LOOP;
END;

Alguno habrá notado la diferencia, ¿no?. ;-) Expliquemos


detalladamente este código para acabar de entenderlo:

Usamos un bucle FOR, así que la variable que hace de índice NO se


declara. Los elementos del cursor se referenciarán como siempre:
fila.codigo y fila.nombre. La apertura, el FETCH y el cierre del cursor lo
hace automáticamente el bucle FOR (una faena menos). Cuando se termian de
recorrer el cursor, el bucle termina el solito. ¿Se puede pedir algo más?.

Pág 15 de 16
Curso de PL/SQL

¿Y si pudiera no declarar el cursor? Buff!! pues claro!!, atención:

BEGIN
--- Bucle FOR de cursor
FOR fila IN (SELECT codigo, nombre
FROM EMPLEADOS
WHERE departamento=3) LOOP
... sentencias para tratar los datos
END LOOP;
END;

Vaya, pues hemos dejado el código en nada o casi nada. (Ya dije que
yo utilizaba los bucles FOR para los cursores, ¿verdad?).

Para ser honestos, este último ejemplo tiene alguna “pega” en


algunos casos. La primera es que el cursor NO está declarado, luego solo
existe dentro del bucle (A veces esto os dará igual, ¿a que sí?). La
segunda es que al estar el cursor definido solo dentro del bucle NO se
pueden consultar sus atributos, aunque como veis, tampoco nos ha hecho
mucha falta. De todas formas, la forma más habitual es la aquella en la
que el cursor se declara y luego se construye un bucle FOR, más que nada
por la “reutilización del código”.

Para terminar, recordaros que la SELECT del cursor puede ser todo lo
compleja que queráis, pudiendo utilizar tablas y vistas. Yo os he puesto
SELECT's muy sencillitas pero en el siguiente capítulo (“Conceptos
avanzados de cursores explícitos”) donde veremos, entre otras cosas, los
cursores con parámetros, ya pondremos más “tela”.

Pág 16 de 16