Control de Flujo de Ejecución
Se puede cambiar la ejecución lógica de las sentencias dentro de un bloque PL/SQL a través de Estructuras de Control. Una estructura de control consta de un punto de entrada y uno de salida.
Las Estructuras de Selección: verifican cierta condición, después ejecutan cierta secuencia de expresiones dependiendo si la condición resultó ser verdadera o falsa. Una condición es cualquier variable o expresión que retorna un valor booleano (TRUE, FALSE o NULL).
Expresiones CASE: retornan un resultado basado en una o más alternativas.
Sentencias CASE: evalúan una condición y realiza una acción.
Las Estructuras de Iteración: ejecutan una secuencia de sentencias repetidamente mientras la condición permanezca verdadera.
Sentencia IF
La estructura de control de PL/SQL IF es similar a la estructura IF de otros lenguajes procedurales. Permiten realizar acciones de forma selectiva basado en la evaluación de condiciones.
En la Sintaxis:
- condición: es una variable o expresión booleana que retorna TRUE, FALSE o NULL.
- THEN: Asocia la expresión Booleana con la secuencia de instrucciones que continúan.
- sentencias: puede ser una o más sentencias PL/SQL o SQL. Las sentencias son ejecutadas sólo si la condición es evaluada como TRUE.
- ELSIF: Es una palabra que introduce condiciones adicionales si la primera condición retorna FALSE o NULL.
- ELSE: Introduce la cláusula por defecto que es ejecutada sólo si ninguno de las condiciones anteriores es TRUE.
- END IF: Marca el fin de una sentencia IF.
DECLARE v_mi_edad number(2) := 31; BEGIN IF v_mi_edad < 11 THEN DBMS_OUTPUT.PUT_LINE('Yo soy un niño'); END IF; END; DECLARE v_mi_edad number(2) := 10; v_mi_nombre varchar2(20) := 'Luis'; BEGIN IF v_mi_edad < 11 AND v_mi_nombre = 'Luis' THEN DBMS_OUTPUT.PUT_LINE('Yo soy un niño que se llama Luis'); END IF; END;
Sentencia IF Simple En el bloque PL/SQL del primer ejemplo, la variable v_mi_edad es inicializada en 31. La condición de la sentencia IF retorna FALSE, porque v_mi_edad no es menor que 11 por lo tanto el control nunca alcanza la cláusula THEN sino que la ejecución del bloque continúa después del END IF.
En el bloque del segundo ejemplo, la condición usa el operador AND por lo tanto, por lo tanto la condición completa es evaluada como TRUE sólo si ambas condiciones son evaluadas como TRUE. Por lo tanto en este caso como ambas condiciones son verdaderas (TRUE) el control alcanza la cláusula THEN y el bloque muestra el mensaje Yo soy un niño que se llama Luis
DECLARE v_salario_min NUMBER(4); BEGIN SELECT MIN(SALARY) INTO v_salario_min FROM employees; DBMS_OUTPUT.PUT_LINE('El salario mínimo es de : ' || TO_CHAR(v_salario_min,'$999,999')); IF v_salario_min > 3000 THEN DBMS_OUTPUT.PUT_LINE('Por lo tanto está dentro del rango normal'); ELSE DBMS_OUTPUT.PUT_LINE('Por lo tanto debe ser aumentado'); END IF; END;
Sentencia IF THEN ELSE En el bloque PL/SQL, se obtiene el salario mínimo entre todos lo empleados el que se visualiza una vez obtenido. Posteriormente se ejecuta la condición de la sentencia IF la que retorna FALSE porque v_salario_min no es mayor a 3000 por lo tanto el control pasa a la cláusula ELSE y se mostrará el mensaje Por lo tanto debe ser aumentado.
DECLARE v_mi_edad number(2) := 30; v_mi_nombre varchar2(20) := 'Luis'; BEGIN IF v_mi_edad < 11 AND v_mi_nombre = 'Luis' THEN DBMS_OUTPUT.PUT_LINE('Yo soy un niño que se llama Luis'); ELSE DBMS_OUTPUT.PUT_LINE('Yo no soy un niño pero me llamo Luis'); END IF; END;
Sentencia IF THEN ELSE En el bloque PL/SQL del ejemplo, la condición usa el operador AND por lo tanto, esta condición es evaluada como TRUE sólo si ambas condiciones son evaluadas como TRUE. En este caso como sólo una de las condiciones es verdadera el control pasa a la instrucción ELSE y el bloque muestra el mensaje Yo no soy un niño pero me llamo Luis
DECLARE v_mi_edad number(2) :=31; BEGIN IF v_mi_edad < 20 THEN DBMS_OUTPUT.PUT_LINE('Yo soy un niño'); ELSIF v_mi_edad < 30 THEN DBMS_OUTPUT.PUT_LINE('Yo estoy en mis veintes'); ELSIF v_mi_edad < 40 THEN DBMS_OUTPUT.PUT_LINE('Yo estoy en mis treintas'); ELSE DBMS_OUTPUT.PUT_LINE('Yo siempre seré joven'); END IF; END;
Sentencia IF THEN ELSE El bloque PL/SQL del ejemplo, valida el valor de la variable v_mi_edad. La cláusula IF contiene múltiples cláusulas ELSIF y un ELSE. La condición para ELSIF son seguidas por la cláusula THEN que es ejecutada si la condición del ELSIF retorna TRUE. Cuando se tienen múltiples cláusulas ELSIF, si la primera condición es FALSE o NULL el control pasa a la siguiente cláusula ELSIF hasta que alguna de ellas retorne TRUE. Las condiciones son evaluadas una a una. Si todas son FALSE o NULL se ejecutarán las sentencias de la cláusula ELSE si existe.
DECLARE v_sal_max NUMBER(5); BEGIN SELECT MAX(salary) INTO v_sal_max FROM employees; IF v_sal_max < 5000 THEN DBMS_OUTPUT.PUT_LINE('Salario máximo menor a 5000'); ELSIF v_sal_max < 10000 THEN DBMS_OUTPUT.PUT_LINE('Salario máximo menor a 10000 y mayor = a 5000'); ELSIF v_sal_max < 15000 THEN DBMS_OUTPUT.PUT_LINE('Salario máximo menor a 15000 y mayor = a 10000'); ELSIF v_sal_max < 20000 THEN DBMS_OUTPUT.PUT_LINE('Salario máximo menor a 20000 y mayor = a 15000'); ELSE DBMS_OUTPUT.PUT_LINE('Salario máximo es mayor a 20000'); END IF; END;
Sentencia IF THEN ELSE En el ejemplo, se valida el salario máximo obtenido desde tabla employees. Las cláusulas son evaluadas una a una hasta que alguna de ellas retorne TRUE. Si todas la condiciones son FALSE o NULL se ejecutarán las sentencias de la cláusula ELSE (si existe). En el ejemplo, el salario máximo es 24100 por lo tanto el bloque mostrará el mensaje Salario máximo es mayor a 20000 de la cláusula ELSE.
DECLARE v_mi_edad number; BEGIN IF v_mi_edad < 11 THEN DBMS_OUTPUT.PUT_LINE('Yo soy un niño '); ELSE DBMS_OUTPUT.PUT_LINE('Yo no soy un niño '); END IF; END;
Valores Nulos en Sentencias IF En el bloque PL/SQL, la variable v_mi_edad es declarada pero no inicializada. La condición en la sentencia IF retorna NULL (en lugar de TRUE o FALSE) por lo tanto, en este caso, el control se entrega a la sentencia ELSE visualizando el bloque el mensaje Yo no soy un niño.
Expresiones CASE
Una expresión CASE retorna un resultado basado en una o más alternativas. Para devolver el resultado, la expresión CASE utiliza un selector, que es una expresión cuyo valor se utiliza para devolver una de varias alternativas. El selector es seguido por una más cláusulas WHEN que se comprueban secuencialmente. El valor del selector determina qué resultado se devuelve. Si el valor del selector es igual al valor de una expresión de la cláusula WHEN, la cláusula WHEN se ejecuta y que se retorna el resultado.
CASE [ selector ] WHEN expresión1 THEN resultado1 WHEN expresión2 THEN resultado2 ... WHEN expresiónN THEN resultadoN [ ELSE resultadoN+1 ] END;En la sintaxis:
- Para retornar el resultado, la expresión CASE usa un selector y una expresión cuyo valor es usado para retornar uno de las diferentes alternativas.
- El selector es seguido de una o más cláusulas WHEN que son chequeadas secuencialmente.
- El valor del selector determina que resultado se retorna.
- Si el valor del selector es igual al valor de la expresión de la cláusula WHEN ésta es ejecutada y el resultado correspondiente es retornado.
- Las cláusulas WHEN contiene condiciones de búsqueda que devuelven un valor Booelano.
DECLARE v_calidad varchar2(1) := 'A'; v_valoracion varchar2(20); BEGIN v_valoracion := CASE v_calidad WHEN 'A' THEN ' Excelente' WHEN 'B' THEN ' Muy bueno' WHEN 'C' THEN ' Bueno' ELSE 'No existe calidad' END; DBMS_OUTPUT.PUT_LINE ('Calidad: '|| v_calidad || ' Valoración:' || v_valoracion); END;
Expresiones CASE En el bloque del ejemplo, el selector v_calidad determina el valor que la variable v_valoracion tendrá. En este caso, será el valor ‘Excelente’, ya que la variable calidad se ha inicializa en ‘A’.
DECLARE v_salario_prom NUMBER(5); v_nombre_dep VARCHAR2(30); v_valoracion VARCHAR2(15); BEGIN SELECT ROUND(AVG(emp.salary)), dep.department_name INTO v_salario_prom, v_nombre_dep FROM employees emp JOIN departments dep ON(emp.department_id = dep.department_id) WHERE emp.department_id = 50 GROUP BY dep.department_name ; v_valoracion := CASE WHEN v_salario_prom > 5000 THEN ' Exelente' WHEN v_salario_prom > 3000 THEN ' Bueno' ELSE 'Regular' END; DBMS_OUTPUT.PUT_LINE ('El salario promedio del departamento 50 ' || v_nombre_dep || ' es de ' || TO_CHAR(v_salario_prom, '9,999') || ' calificado de ' || v_valoracion); END;
Expresiones CASE En el ejemplo, no existe un selector ya que en este caso las cláusulas WHEN contienen una expresión de testeo que retornará un valor booleano. Cuando la expresión de testeo o búsqueda retorne TRUE su resultado será asignado a la variable v_valoracion según el salario promedio.
DECLARE v_deptid NUMBER(3):=30; v_deptnom VARCHAR2(20); v_total_emp NUMBER(5); BEGIN CASE v_deptid WHEN 30 THEN SELECT COUNT(employee_id), department_name INTO v_total_emp, v_deptnom FROM employees JOIN departments USING (department_id) WHERE department_id = v_deptid GROUP BY department_name ; END CASE; DBMS_OUTPUT.PUT_LINE ('En el departamento '|| v_deptnom|| ' trabaja un total de ' || v_total_emp || ' empleados'); END;
Sentencias CASE En el bloque, se valida el valor de la variable v_deptid. Si el valor es 30 entonces se obtiene el total de empleados que trabajan en ese departamento y el nombre del departamento para posteriormente visualizar el mensaje de salida.
DECLARE v_sal_prom NUMBER(5); BEGIN SELECT ROUND(AVG(salary)) INTO v_sal_prom FROM employees; CASE WHEN v_sal_prom < 5000 THEN UPDATE employees SET salary = salary * 1.25 WHERE salary < v_sal_prom; WHEN v_sal_prom < 7000 THEN UPDATE employees SET salary = salary * 1.10 WHERE salary < v_sal_prom; ELSE DBMS_OUTPUT.PUT_LINE('No corresponde aumento de salario'); END CASE; END;
Sentencias CASE En el ejemplo, el bloque obtiene el salario promedio entre todos los empleados. La sentencia CASE evalúa el valor del salario promedio obtenido para actualizar el salario a los empleados que corresponda según la condición dada.
Diferencias entre CASE
Expresiones CASE: Evalúan la condición y retorna un valor. Terminan con END.
Sentencias CASE: Evalúan la condición y realiza una acción. La sentencia puede ser un bloque completo PL/SQL. Terminan con END CASE.
Manejo de Valores Nulos
Las comparaciones simples que involucran nulos siempre retornan NULL.
Para preguntar si una variable no posee un valor NULO se debe usar el operador lógico NOT (NOT NULL).
En sentencias de control condicionales, si la condición retorna NULL, la secuencia de sentencias asociadas no son ejecutadas.
DECLARE x NUMBER:= 5; y NUMBER:= NULL; BEGIN IF x != y THEN DBMS_OUTPUT.PUT_LINE('x es distinto de y'); ELSE DBMS_OUTPUT.PUT_LINE('x es distinto de y pero la comparación es NULL'); END IF; END;
Manejo de Valores Nulos En el bloque PL/SQL, la condición de comparación entre las variables x e y retorna NULL no TRUE, por lo tanto la secuencias de sentencias después del THEN no serán ejecutadas, se ejecutarán las sentencias después del ELSE.
DECLARE a NUMBER:= NULL; b NUMBER:= NULL; BEGIN IF a = b THEN DBMS_OUTPUT.PUT_LINE('a es igual a b'); ELSE DBMS_OUTPUT.PUT_LINE('a y b son NULOS pero la comparación es NULL NO TRUE'); END IF; END;
Manejo de Valores Nulos En el bloque PL/SQL, la condición retorna NULL no TRUE, por lo tanto la secuencias de sentencias después del THEN no serán ejecutadas, se ejecutarán las sentencias después del ELSE.
DECLARE a number:= NULL; b number:=NULL; BEGIN IF a IS NOT NULL AND b IS NOT NULL THEN DBMS_OUTPUT.PUT_LINE('a y b no son nulos'); ELSIF a IS NULL AND b IS NULL THEN DBMS_OUTPUT.PUT_LINE('a y b son nulos'); END IF; END;
Manejo de Valores Nulos En la condición del bloque PL/SQL se pregunta si los valores de las variables son Nulos o no. Como ambas variables han sido inicializadas NULAS la sentencia que se ejecutará es la que se especifica a continuación de la instrucción THEN del ELSIF.
Controles de Iteración (Bucles)
PL/SQL ofrece varias facilidades para estructurar bucles y poder repetir una instrucción o secuencia de instrucciones varias veces. Los loops se utilizan principalmente para ejecutar instrucciones de forma repetida hasta que se alcanza una condición de salida. Es obligatorio tener una condición de salida en un loop, de lo contrario, es infinito.
Las construcciones de loop o bucle son el segundo tipo de estructura de control. PL/SQL proporciona los siguientes tipos de loops:
- Loop Básico que realiza acciones repetitivas sin condiciones generales.
- FOR loops que realizan acciones iterativas basadas en un recuento.
- WHILE loops que realizan acciones iterativos basados en una condición.
El comando EXIT se puede utilizar para terminar loops. Un loop básico debe tener una salida.
Loop Básico
La forma más simple de una sentencia LOOP es el Loop Básico, que encierra una serie de declaraciones entre las palabras reservadas LOOP y END LOOP . Cada vez que el flujo de ejecución llega a la instrucción END LOOP se devuelve el control a la sentencia LOOP correspondiente. Un loop básico permite la ejecución de sus sentencias al menos una vez, incluso si la condición de salida ya se cumple al entrar en el loop .
Sin la instrucción EXIT, el loop sería infinito . Se puede usar la sentencia EXIT para terminar el loop. El control pasa a la siguiente sentencia después de la sentencia END LOOP. Si a la sentencia EXIT se agrega una Condición WHEN significa que el LOOP se ejecutará hasta que se cumpla la condición. Un loop básico puede contener múltiples sentencias EXIT, pero se recomienda que sólo tener un punto de salida.
En la sintaxis:- sentencia: pueden ser uno o más sentencias PL/SQL o SQL.
- condición: es una variable Booleana o expresión
DECLARE v_countryid locations.country_id%TYPE := 'CA'; v_loc_id locations.location_id%TYPE; v_contador NUMBER(2) := 1; v_new_city locations.city%TYPE := 'Montreal'; BEGIN SELECT MAX(location_id) INTO v_loc_id FROM locations WHERE country_id = v_countryid; LOOP INSERT INTO locations(location_id, city, country_id) VALUES((v_loc_id + v_contador), v_new_city, v_countryid); v_contador := v_contador + 1; EXIT WHEN v_contador > 3; END LOOP; END;
Loop Básico El bloque PL/SQL del ejemplo inserta nuevas filas a la tabla locations. La inserción de nuevas filas se efectuará 3 veces ya que el LOOP es controlado a través de la variable contador. Cuando esta variable sea mayor a 3 el control pasará a la sentencia que continúa después del END LOOP, en este caso se acaba el bloque PL/SQL.
WHILE condición LOOP sentenciaN; END LOOP;
WHILE Loop Se puede utilizar el WHILE Loop para repetir una secuencia de sentencias hasta que la condición de control no sea verdadera. La condición se evalúa al inicio de cada iteración. El ciclo termina cuando la condición es FALSE o NULL. Si la condición es FALSE o NULL en el inicio del loop, no se realizan más iteraciones. Por lo tanto, es posible que ninguna de las instrucciones dentro del loop se ejecuta. En la sintaxis: condición: es una variable Booleana o expresión. sentencia: pueden ser uno o más sentencias PL/SQL o SQL.
DECLARE v_countryid locations.country_id%TYPE := 'CA'; v_loc_id locations.location_id%TYPE; v_new_city locations.city%TYPE := 'Montreal'; v_contador NUMBER := 1; BEGIN SELECT MAX(location_id) INTO v_loc_id FROM locations WHERE country_id = v_countryid; WHILE v_contador <= 3 LOOP INSERT INTO locations(location_id, city, country_id) VALUES((v_loc_id + v_contador), v_new_city, v_countryid); v_contador:= v_contador + 1; END LOOP; END;
WHILE Loop Basados en el ejemplo anterior, ahora el ciclo iterativo es manejado usando WHILE LOOP. La condición ahora es evaluada al comienzo de cada iteración a través de la variable contador. Cuando esta variable sea mayor a 3 el control pasará a la sentencia que continúa después del END LOOP.
FOR contador IN [REVERSE] límite_inferior .. límite_superior LOOP sentenciaN; END LOOP;
FOR Loop FOR Loop tiene la misma estructura general que el Loop básico. Además, tienen una instrucción de control antes de la palabra clave LOOP para establecer el número de iteraciones que realiza PL/SQL. Las iteraciones se efectúan un número finito y conocido de veces. El contador es declarado implícitamente y sólo pude ser usado dentro del loop. La sintaxis ‘límite_inferior .. límite_superior’ es obligatoria. Pueden ser literales, variables o expresiones pero que deben ser números enteros. En la sintaxis: contador: es un entero declarado implícitamente (sólo involucra al loop y posteriormente se elimina) cuyo valor incrementa o disminuya de manera automática en uno en cada iteración del loop hasta que se alcanza el límite_inferiror o límite_superior. REVERSE: causa que el contador disminuya con cada iteración desde el límite_superior al límite_inferior. límite_inferior: específica el valor inferior del contador. límite_superior: especifica el valor superior del contador
DECLARE v_countryid locations.country_id%TYPE := 'CA'; v_loc_id locations.location_id%TYPE; v_new_city locations.city%TYPE := 'Montreal'; BEGIN SELECT MAX(location_id) INTO v_loc_id FROM locations WHERE country_id = v_countryid; FOR i IN 1..3 LOOP INSERT INTO locations(location_id, city, country_id) VALUES((v_loc_id + i), v_new_city, v_countryid ); END LOOP; END;
FOR Loop En el bloque ahora el ciclo iterativo se maneja con FOR LOOP. La variable i es declara implícitamente para ser usada en el loop y su valor irá desde el límite inferior (1) al límite superior (3) que indica la cantidad de iteraciones del loop. Una vez que se haya llegado al valor_máximo del loop el control pasará a la sentencia que continúa después del END LOOP.
DECLARE v_countryid locations.country_id%TYPE := 'CA'; v_loc_id locations.location_id%TYPE; v_new_city locations.city%TYPE := 'Montreal'; v_limite_inf NUMBER(1):=1; v_limite_sup NUMBER(1):=3; BEGIN SELECT MAX(location_id) INTO v_loc_id FROM locations WHERE country_id = v_countryid; FOR i IN v_limite_inf .. v_limite_sup LOOP INSERT INTO locations(location_id, city, country_id) VALUES((v_loc_id + i), v_new_city, v_countryid ); END LOOP; END;
FOR Loop Basados en el mismo ejemplo anterior, en este bloque PL/SQL el límite inferior (1) y el límite superior (3) del loop está dado por las variables v_limite_inf y v_limite_sup respectivamente. Una vez que se haya llegado al valor_máximo del loop el control pasará a la sentencia que continúa después del END LOOP.
Pautas para Loops
Usar el control de iteración LOOP básico cuando las sentencias contenidas dentro del bucle deben ser ejecutas a lo menos una vez.
Usar WHILE Loop si la condición tiene que ser evaluada al comienzo de cada iteración.
Usar FOR Loop si el número de iteraciones es conocido.
Loops Anidados y Etiquetas
- Los loops anidados tienen múltiples niveles.
- El uso de etiquetas permiten distinguir entre bloques y loops.
- Se pueden anidar loops básicos, FOR, While dentro de otro.
- La etiqueta debe ir antes de la palabra LOOP palabra dentro de delimitadores de la etiqueta (la etiqueta << >>). En loops FOR o WHILE coloque la etiqueta antes de FOR o WHILE.
En el bloque del ejemplo hay dos ciclos iterativos. El loop externo es identificado con la etiqueta <
Sentencia PL/SQL CONTINUE
La sentencia CONTINUE permite transferir el control dentro de un loop a una nueva iteración o para salir del loop. Muchos otros lenguajes de programación tienen esta funcionalidad. Con Oracle 11g, PL/SQL también ofrece esta funcionalidad. En versiones anteriores a Oracle 11g, se puede codificar una solución utilizando variables booleanas e instrucciones condicionales para simular la funcionalidad de CONTINUE.
La sentencia CONTINUE ofrece un medio sencillo para controlar las iteraciones de loop. Puede ser más eficiente que soluciones de codificación anteriores. La sentencia CONTINUE no puede aparecer fuera de un loop porque esto genera un error del compilador.
DECLARE v_total SIMPLE_INTEGER := 0; BEGIN FOR i IN 1..10 LOOP v_total := v_total + i; dbms_output.put_line('Total al comienzo del Loop es: '|| v_total); CONTINUE WHEN i > 5; v_total := v_total + i; dbms_output.put_line('Total despues de CONTINUE es: '|| v_total); END LOOP; END;
Sentencia PL/SQL CONTINUE La primera asignación a la variable v_total es ejecutada para cada uno de los 10 iteraciones del loop. La segunda asignación a la variable v_total se ejecuta durante las primeros cinco iteraciones del loop. La sentencia CONTINUE transfiere el control a una nueva iteración del loop cuando i sea mayor a 5, por lo que durante las cinco últimas iteraciones del loop, la segunda asignación a la variable v_total no se ejecuta. El resultado final de la variable v_total es 70.
DECLARE v_total NUMBER := 0; BEGIN <> FOR i IN 1..10 LOOP v_total := v_total + 1; dbms_output.put_line('Total es: ' || v_total); FOR j IN 1..10 LOOP CONTINUE LoopPrincipal WHEN i + j > 5; v_total := v_total + 1; END LOOP; END LOOP; END;
Sentencia PL/SQL CONTINUE Se puede utilizar la instrucción CONTINUE para saltar a la siguiente iteración de un loop exterior. Para esto, el loop exterior debete tener etiqueta para identificar dónde la sentencia CONTINUE debe pasar el control. La sentencia CONTINUE termina el loop interno cuando la condición WHEN es verdadera (al igual que si se usara EXIT). Después de que el loope más interior es terminado por la sentencia CONTINUE, el control se transfiere a la siguiente iteración del loop más externo que en el ejemplo está marcado con la etiqueta LoopPrincipal. Cuando este par de loop se completa, el valor de la variable v_total es 20. También se puede utilizar la instrucción CONTINUE dentro de un bloque interno que no contiene un loop mientras el bloque esté anidado dentro de un loop externo.
No hay comentarios:
Publicar un comentario