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 contador porcentaje fecha_alta correcto VARCHAR2(30); NUMBER(6) NOT NULL := 0 ; NUMBER(3) DEFAULT 16 ; DATE DEFAULT sysdate ; 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 cambie su valor. declarar una constante, prohibiendo así que se 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 opinión. útil, pero parece que no tanto, ¿verdad?. Bueno es mi 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 componentes: • • PL/SQL es similar a un vector y debe tener dos 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 SQL%FOUND SQL%NOTFOUND SQL%ISOPEN Número de filas afectadas (es un valor entero) Atributo booleano que da como resultado TRUE si la sentencia SQL mas reciente afecta a una ó más filas. Atributo booleano que da como resultado TRUE si la sentencia SQL mas reciente no afecta a ninguna fila. 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 2 25 4 Pepito Boss Manolito Programmer Juanito Administrator Luisito Perez <---- puntero actual Con el primer FETCH, el puntero se sitúa en la segunda fila y hemos recuperado la primera fila. 1 2 25 4 Pepito Boss Manolito Programmer Juanito Administrator Luisito Perez <---- puntero actual 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 %FOUND %NOTFOUND %ISOPEN Número de filas afectadas (es un valor entero) Atributo booleano que da como resultado TRUE si la sentencia SQL mas reciente afecta a una ó más filas. Atributo booleano que da como resultado TRUE si la sentencia SQL mas reciente no afecta a ninguna fila. 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 RECORD?, ¿no? Pues ahí va: el ejercicio de definir un cursor con un 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?. detalladamente este código para acabar de entenderlo: Usamos un bucle FOR, así que la declara. Los elementos del cursor fila.codigo y fila.nombre. La apertura, hace automáticamente el bucle FOR (una recorrer el cursor, el bucle termina el ;-) Expliquemos variable que hace de índice NO se se referenciarán como siempre: el FETCH y el cierre del cursor lo faena menos). Cuando se termian de 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