Construyendo con Bloques en PL/SQL

Por Steven Feuerstein Oracle Ace
Traducido por Alfonso Vicente
Publicado en marzo 2012

Publicado en

Parte 1 en una serie de artículos sobre la comprensión y uso de PL/SQL

El lenguaje PL/SQL de Oracle celebró su cumpleaños número 22 en 2011. Lo sé porque estoy mirando la primera guía del usuario de Oracle PL/SQL que se haya publicado, es de la versión 1.0 de PL/SQL, y su fecha de publicación es Setiembre de 1989. Estaba trabajando para Oracle en ese momento, construyendo las primeras herramientas de automatización utilizadas por la fuerza de ventas de Oracle EE.UU. Ya había trabajado con PL/SQL dentro de SQL Forms 3.0, pero fue con el lanzamiento de la base de datos Oracle 6, que PL/SQL estuvo disponible como un lenguaje independiente para desarrollo de aplicaciones.

Tres años más tarde, escribí mi primer libro sobre PL/SQL y desde entonces he estudiado este lenguaje, he desarrollado montones y montones de código PL/SQL, y he escrito sobre este excelente lenguaje de programación de base de datos. Por supuesto, yo no era el único. Miles de desarrolladores en todo el mundo han construido una multitud de aplicaciones basadas en Oracle PL/SQL en las décadas desde que se liberó.

Lo mejor de todo es que sigue habiendo un flujo constante de nuevos desarrolladores PL/SQL. De hecho, con la aparición relativamente reciente de la India, China y otras naciones como potencias de tecnología, he visto a toda una nueva generación de desarrolladores descubrir PL/SQL y trabajar para dominarlo.

Para ayudar a que los recién llegados a PL/SQL saquen el máximo provecho de este lenguaje, Oracle Magazine me ha pedido que escriba una serie de artículos para principiantes, de los cuales éste es el primero. Si usted es un desarrollador experimentado en PL/SQL, también puede encontrar en estos artículos un repaso práctico sobre sus fundamentos.

Para esta serie de artículos, voy a suponer que a pesar de ser nuevos en PL/SQL, mis lectores han tenido alguna experiencia en programación y están familiarizados con SQL. Mi enfoque general, además, estará en conseguir desarrolladores productivos en PL/SQL lo más rápido posible.

¿Qué es PL/SQL?

Para contestar esta pregunta, es importante recordar que en cada sitio Web que visitamos, cada aplicación que se ejecuta es construida sobre un stack (una pila) de tecnologías de software. En la parte superior de la pila está la capa de presentación, las pantallas o dispositivos interactivos con los que el usuario interactúa directamente (hoy en día los lenguajes más populares para las capas de presentación son Java y .NET). En la parte inferior de la pila está el lenguaje de máquina que se comunica con el hardware.

En algún lugar en medio de la pila de tecnología se encuentra la base de datos, el software que nos permite almacenar y manipular grandes volúmenes de datos complejos. La tecnología de bases de datos relacionales, construida en torno a SQL, es la tecnología de base de datos dominante en el mundo de hoy.

SQL es un lenguaje de conjuntos muy poderoso, cuyo único objetivo es manipular el contenido de bases de datos relacionales. Si usted desarrolla aplicaciones basadas en bases de datos Oracle, usted (o el código de alguien que escribe en un nivel inferior de la pila de la tecnología) debe ejecutar sentencias SQL para recuperar datos desde o cambiar datos en la base de datos. Sin embargo, SQL no se puede utilizar para implementar toda la lógica de negocios y la funcionalidad que el usuario final necesita en nuestras aplicaciones. Esto nos lleva a PL/SQL.

PL/SQL significa Procedural Language/Structured Query Language (una extensión de programación estructurada sobre SQL). PL/SQL ofrece un conjunto de instrucciones clásicos de la programación estructurada (instrucción condicional IF, loops o iteraciones, asignaciones), organizado dentro de bloques (lo que se explica más adelante), que complementan y amplían el alcance de SQL.

Sin duda que es posible crear aplicaciones sobre Oracle y SQL sin usar PL/SQL. Sin embargo, utilizar PL/SQL para realizar operaciones específicas de bases de datos, particularmente la ejecución de sentencias SQL, ofrece varias ventajas, incluyendo una estrecha integración con SQL, un mejor rendimiento a través del tráfico de red reducido, y la portabilidad (los programas PL/SQL pueden correr en cualquier instancia de base de datos Oracle). Por lo tanto, el código del front-end de muchas aplicaciones ejecuta tanto sentencias SQL como bloques PL/SQL, para maximizar el rendimiento al tiempo que mejora la capacidad de mantenimiento de las aplicaciones.

Construyendo bloques de programas PL/SQL

PL/SQL es un lenguaje estructurado con bloques. Un bloque PL/SQL es definido por las palabras clave DECLARE, BEGIN, EXCEPTION, y END, que dividen el bloque en tres secciones

1. Declarativa: sentencias que declaran variables, constantes y otros elementos de código, que después pueden ser usados dentro del bloque
2. Ejecutable: sentencias que se ejecutan cuando se ejecuta el bloque
3. Manejo de excepciones: una sección especialmente estructurada para atrapar y manejar cualquier excepción que se produzca durante la ejecución de la sección ejecutable

Sólo la sección ejecutable es obligatoria. No es necesario que usted declare nada en un bloque, ni que maneje las excepciones que se puedan lanzar.

Un bloque es en sí mismo una sentencia ejecutable, por lo que se pueden anidar los bloques unos dentro de otros.

Aquí hay algunos ejemplos:

El clásico “¡Hola Mundo!” es un bloque con una sección ejecutable que llama al procedimiento DBMS_OUTPUT.PUT_LINE para mostrar texto en pantalla:

BEGIN
  DBMS_OUTPUT.put_line('¡Hola Mundo!');
END;

Las funciones y procedimientos —tipos de bloques con un nombre— son discutidos con mayor detalle más adelante en este artículo, así como los paquetes. En pocas palabras, sin embargo, un paquete es un contenedor de múltiples funciones y procedimientos. Oracle extiende PL/SQL con muchos paquetes incorporados en el lenguaje.

El siguiente bloque declara una variable de tipo VARCHAR2 (un string) con un largo máximo de 100 bytes para contener el string ‘¡Hola Mundo!’. Después, el procedimiento DBMS_OUTPUT.PUT_LINE acepta la variable, en lugar del literal, para desplegarlo:

DECLARE
  l_mensaje VARCHAR2(100) := '¡Hola Mundo!';
BEGIN
  DBMS_OUTPUT.put_line(l_mensaje);
END;

Note que he llamado a la variable l_mensaje. Normalmente uso el prefijo l_ para variables locales —variables definidas dentro del código de un bloque— y el prefijo g_ para variables globales definidas en un paquete.

El siguiente ejemplo de bloque agrega una sección de manejo de excepciones que atrapa cualquier excepción (WHEN OTHERS) que pueda ser lanzada y muestra el mensaje de error, que es retornado por la función SQLERRM (provista por Oracle).

DECLARE
  l_mensaje VARCHAR2(100) := '¡Hola Mundo!';
BEGIN
  DBMS_OUTPUT.put_line(l_mensaje);
EXCEPTION
  WHEN OTHERS
    THEN
      DBMS_OUTPUT.put_line(SQLERRM);
END;

El siguiente ejemplo de bloque demuestra la habilidad de PL/SQL de anidar bloques dentro de bloques así como el uso del operador de concatenación (||) para unir múltiples strings.

DECLARE
  l_mensaje VARCHAR2(100) := '¡Hola';
BEGIN
  DECLARE
    l_mensaje2 VARCHAR2(100) := l_mensaje || ' Mundo!'; 
  BEGIN
    DBMS_OUTPUT.put_line(l_mensaje2);
  END;
EXCEPTION
  WHEN OTHERS
    THEN
      DBMS_OUTPUT.put_line(DBMS_UTILITY.format_error_stack);
END;
  

Ejecutando bloques PL/SQL

Una vez que hemos escrito un bloque de código PL/SQL éste se puede ejecutar. Existen muchas herramientas para ejecutar código PL/SQL. La más básica es SQL*Plus, una interfaz de línea de comandos para ejecutar sentencias SQL así como bloques PL/SQL. La Figura 1 muestra un ejemplo de ejecución del más simple de los bloques de ejemplo de nuestro “¡Hola Mundo!” en SQL*Plus.

Ejecutando bloques PL/SQL
Figura 1: Ejecutando “¡Hola Mundo!” en SQL*Plus

La primera cosa que hacemos después de conectarnos a la base mediante SQL*Plus es habilitar la salida del servidor, por lo que las llamadas a DBMS_OUTPUT.PUT_LINE resultarán en la visualización de texto en la pantalla. Luego escribimos el código que constituye nuestro bloque. Finalmente, ingresamos una barra (/) para decirle a SQL*Plus que ejecute ese código.

SQL*Plus entonces ejecuta el bloque y muestra el “¡Hola Mundo!” en la pantalla. SQL*Plus es provisto por Oracle como una especie de línea base de entorno donde se pueden ejecutar sentencias SQL y bloques PL/SQL. Mientras que algunos desarrolladores siguen utilizando únicamente SQL*Plus, la mayoría utiliza un entorno de desarrollo integrado (IDE). Entre los más populares de estos entornos de desarrollo (basado en encuestas informales que he tomado en mis sesiones de entrenamiento) son

• Oracle SQL Developer, de Oracle • Toad y SQL Navigator, de Quest Software • PL/SQL Developer, de Allround Automations

Cada herramienta ofrece, con algunas diferencias, ventanas y pasos para crear, guardar, y ejecutar bloques PL/SQL, así como habilitar y deshabilitar la salida del servidor. En esta serie de artículos, voy a suponer que sólo tienen acceso a SQL*Plus y que van a ejecutar todas las sentencias en una ventana de comandos SQL*Plus.

¡Póngale nombre a los bloques!

Todos los bloques que hemos visto hasta el momento son “anónimos”, no tienen nombres. Si los bloques anónimos fueran la única manera de organizar el código, sería muy difícil usar PL/SQL para crear una aplicación grande y compleja. Por esto, PL/SQL soporta la definición de bloques nombrados (named blocks), también conocidos como subprogramas. Los subprogramas pueden ser procedimientos o funciones. Generalmente, un procedimiento se utiliza para realizar una acción y una función se utiliza para calcular y devolver un valor. Voy a tratar sobre subprogramas con mucho más detalle en un próximo artículo de esta serie. Por ahora, vamos a asegurarnos de que se comprendan los conceptos básicos detrás de la creación del subprograma.

Supongamos que necesitamos mostrar "¡Hola Mundo!" desde múltiples lugares en nuestra aplicación. Queremos evitar la repetición de la misma lógica en todos esos lugares. Por ejemplo, ¿qué pasa cuando tenemos que cambiar el mensaje, tal vez para "¡Hola Universo!"? Vamos a tener que encontrar todos los lugares en nuestro código donde esta lógica aparece.

En su lugar, vamos a crear un procedimiento denominado hola_mundo mediante la ejecución de la siguiente sentencia DDL (Data Definition Language):

CREATE OR REPLACE PROCEDURE hola_mundo IS
  l_mensaje VARCHAR2(100) := '¡Hola Mundo!';
BEGIN
  DBMS_OUTPUT.put_line(l_mensaje);
END hola_mundo;
 

Con esto hemos extendido PL/SQL. Además de llamar a los programas creados por Oracle e instalados en la base de datos (como DBMS_OUTPUT.PUT_LINE), podemos llamar a nuestro propio subprograma dentro de un bloque PL/SQL:

BEGIN
   hola_mundo;
END;
 

Hemos escondido todos los detalles de cómo decir hola al mundo dentro del cuerpo (body), o implementación, de nuestro procedimiento. Ahora podemos llamar a este procedimiento hola_mundo y mostrar el mensaje deseado sin tener que escribir la llamada a DBMS_OUTPUT.PUT_LINE o averiguar la forma correcta de darle formato al texto. Podemos llamar a este procedimiento desde cualquier lugar en nuestra aplicación. Así que si alguna vez necesitamos cambiar ese texto, lo vamos a hacer en un solo lugar, el único punto de definición de ese texto.

El procedimiento hola_mundo es muy simple. Tus procedimientos tendrán mucho más código, y casi siempre también tendrán parámetros. Los parámetros pasan información a los subprogramas, cuando éstos son llamados, y es lo que permite crear subprogramas más flexibles y genéricos. Se pueden usar en muchos contextos diferentes.

He mencionado antes que algún día puede ser que desee mostrar "¡Hola Universo!" en lugar de "¡Hola Mundo!". Podría hacer una copia de nuestro procedimiento hola_mundo y cambiar el texto que se muestra:

CREATE OR REPLACE PROCEDURE hola_universo IS
  l_mensaje VARCHAR2(100) := '¡Hola Universo!';
BEGIN
  DBMS_OUTPUT.put_line(l_mensaje); 
END hola_universo;

Podríamos, sin embargo, terminar con las decenas de variantes del “mismo” procedimiento hola, que haría muy difícil mantener nuestra aplicación. Un enfoque mucho mejor es analizar el procedimiento e identificar qué partes se mantienen incambiadas (son estáticas) cuando el mensaje tiene que cambiar y qué partes cambian. Luego podemos pasar las partes que cambian como parámetros y tener un procedimiento único que se puede utilizar en diferentes circunstancias.

Así que vamos a cambiar hola_mundo (y hola_universo) a un nuevo procedimiento, hola_lugar:

CREATE OR REPLACE PROCEDURE hola_lugar (lugar_in IN VARCHAR2) IS
  l_mensaje  VARCHAR2(100);
BEGIN
  l_mensaje  := '¡Hola ' || place_in;
  DBMS_OUTPUT.put_line(l_mensaje);
END hola_lugar;

Justo después del nombre del procedimiento, añadimos entre paréntesis de apertura y cierre, un único parámetro. Podemos tener varios parámetros, pero cada parámetro de la misma forma básica:

nombre_de_parametro   modo_de_parametro   tipo_de_datos

En otras palabras, debemos proveer un nombre para el parámetro, el modo o forma en que éste será usado (IN = sólo lectura), y el tipo de dato que será pasado al subprograma a través de este parámetro.

En este caso, vamos a pasar un texto de sólo lectura al procedimiento hola_lugar.

Y ahora podemos saludar a nuestro mundo y a nuestro universo como sigue:

BEGIN
   hola_lugar('Mundo!');
   hola_lugar('Universo!');
END;
 

Más adelante en esta serie vamos a explorar el concepto de reutilización y la manera de evitar la repetición, pero debes ser capaz de ver en este ejemplo, el poder de ocultar la lógica detrás de un bloque con nombre.

Ahora supongamos que no sólo queremos mostrar nuestros mensajes "Hola". A veces tenemos que mantener los mensajes en una tabla en la base de datos; en otras ocasiones, tenemos que pasar el texto de nuevo al entorno del cliente para su visualización en un navegador Web. En otras palabras, necesitamos separar la forma en que se construyó el mensaje "Hola" de la forma en que se utiliza (se visualiza, se guarda, se envía otro programa, etc). Podemos alcanzar este nivel deseado de flexibilidad moviendo el código que construye el mensaje a su propia función:

CREATE OR REPLACE FUNCTION 
hola_mensaje(lugar_in IN VARCHAR2) RETURN VARCHAR2 IS
BEGIN
   RETURN '¡Hola ' || place_in;
END hola_mensaje;

Este subprograma se diferencia del procedimiento original en lo siguiente:

• El tipo de programa es ahora FUNCTION y no PROCEDURE.
• El nombre del subprograma ahora describe los datos que se devuelven, no las acciones tomadas.
• El cuerpo o la implementación del subprograma contiene ahora una cláusula RETURN que construye el mensaje y lo devuelve al bloque llamador.
• La cláusula RETURN después de la lista de parámetros establece el tipo de datos devuelto por la función.

Teniendo el código necesario para construir el mensaje dentro de la función hola_mensaje, podemos utilizar este mensaje de múltiples maneras. Podemos, por ejemplo, llamar a la función para obtener el mensaje y asignárselo a una variable:

DECLARE
  l_mensaje  VARCHAR2(100);
BEGIN
  l_mensaje := hola_mensaje('Universo');
END;

Nota que llamamos a la función hola_mensaje como parte de una sentencia PL/SQL (en este caso, la asignación de un texto a una variable). La función hola_mensaje devuelve un string, por lo que se puede utilizar en lugar de un string en cualquier sentencia ejecutable.

También podemos volver a nuestro procedimiento hola_lugar y reemplazar el código utilizado para crear el string con una llamada a la función:

CREATE OR REPLACE PROCEDURE 
hola_lugar(place_in IN VARCHAR2) IS
BEGIN
  DBMS_OUTPUT.put_line(hola_mensaje(place_in));
END hola_lugar;

También podemos llamar la función desde una sentencia SQL. En el siguiente bloque, insertamos el mensaje en una tabla de la base:

BEGIN
  INSERT INTO tabla_mensaje(fecha_mensaje, texto_mensaje)
      VALUES (SYSDATE, hola_mensaje('Montevideo'));
END;

Aunque la lógica del “mensaje hola” es muy simple, demuestra el poder de asignar nombres a una o más sentencias ejecutables (un algoritmo) y luego referenciar el algoritmo simplemente especificando el nombre y los parámetros requeridos.

Los bloques PL/SQL con nombre, permiten construir aplicaciones complejas que pueden ser comprendidas y mantenidas con relativa facilidad.

Sobre los nombres en una base Oracle

Ahora que ya se aprecia la importancia de asignar nombres a la lógica, es tiempo de hablar sobre las reglas para los nombres (o, para ser más precisos, identificadores) tanto en PL/SQL como, de forma más general, en una base Oracle.

Estas son las reglas para construir identificadores válidos en una base Oracle:

• El largo máximo es de 30 caracteres.
• El primer caracter debe ser una letra, pero cada caracter después del primero puede ser una letra, un número (0 a 9), un signo de pesos ($), un guión bajo (_), o un numeral (#). Todos los siguientes son identificadores válidos:

hola_mundo
hola$mundo
hola#mundo

pero estos son inválidos:
1hola_mundo
hola%mundo

• PL/SQL es case-insensitive (no es sensitivo a mayúsculas y minúsculas) con respecto a los identificadores. PL/SQL trata todos los siguientes como el mismo identificador:

hola_mundo
Hola_Mundo
HOLA_MUNDO

Para ofrecer más flexibilidad, Oracle permite evitar las restricciones de la segunda y tercera regla, encerrando al identificador entre comillas dobles. Un quoted identifier (identificador encerrado entre comillas) puede contener cualquier secuencia de caracteres imprimibles excluyendo las comillas dobles; las diferencias entre mayúsculas y minúsculas serán además preservadas. Así, todos los siguientes identificadores son válidos y distintos:

"Abc"
"ABC"
"a b c"

Es muy raro encontrar identificadores entre comillas en código PL/SQL; algunos grupos de desarrollo los usan para conformar con sus convenciones de nombres o porque encuentran que una mezcla de mayúsculas y minúsculas resulta más fácil de leer.

Estas mismas reglas aplican a los nombres de los objetos de base de datos como tablas, vistas y procedimientos, con una regla adicional: a menos que se encierren entre comillas los nombres de estos objetos, Oracle los mantendrá en mayúsculas.

Así que cuando creamos un procedimiento como el que sigue:

CREATE OR REPLACE PROCEDURE hola_mundo IS
BEGIN
  DBMS_OUTPUT.put_line('¡Hola Mundo!');
END hola_mundo;

la base de datos Oracle lo mantendrá con el nombre HOLA_MUNDO.

En el bloque siguiente, llamaremos este procedimiento tres veces, y aunque el nombre luzca diferente cada vez, siempre se ejecutará el mismo procedimiento:

BEGIN
   hola_mundo;
   HOLA_MUNDO;
   "HOLA_MUNDO";
END;

Por otro lado, la base Oracle no será capaz de ejecutar el procedimiento si lo llamamos como sigue:

BEGIN
   "hola_mundo";
END;

Esto buscará dentro de la base un procedimiento llamado hola_mundo en lugar de HOLA_MUNDO.

Si no se quiere que los nombres de los subprogramas se mantengan en mayúsculas, los nombres se deben encerrar entre comillas cuando se crea el subprograma:

CREATE OR REPLACE PROCEDURE "Hola_Mundo" IS
BEGIN
  DBMS_OUTPUT.put_line('¡Hola Mundo!');
END "Hola_Mundo";

Ejecutando SQL dentro de bloques PL/SQL

PL/SQL es un lenguaje de programación de base de datos. Casi todos los programas que escribiremos en PL/SQL leerán desde, o escribirán en, una base de datos Oracle utilizando SQL. Aunque estas series asumen que se conoce SQL, debemos estar conscientes de la forma en que llamamos a las sentencias desde un bloque PL/SQL.

Y aquí hay algunas buenas noticias: Oracle hace que sea muy fácil escribir y ejecutar sentencias SQL en PL/SQL. La mayor parte de las veces, simplemente escribiremos las sentencias SQL directamente en nuestro bloque PL/SQL y después agregaremos el código necesario para la interfaz entre las sentencias SQL y el código PL/SQL.

Supongamos, por ejemplo, que tenemos una tabla llamada empleados, con una columna clave primaria id_empleado, y una columna apellido. Podemos ver el apellido del empleado con ID 138, como sigue:

SELECT apellido
  FROM empleados
 WHERE id_empleado = 138
 

Ahora querríamos ejecutar esta misma consulta dentro de nuestro bloque PL/SQL y desplegar el nombre. Para hacer esto, necesitaremos “copiar” el apellido desde la tabla a una variable local, lo cual podemos hacer con la cláusula INTO:

DECLARE
  v_apellido empleados.apellido%TYPE;
BEGIN
  SELECT apellido
    INTO v_apellido
    FROM empleados
   WHERE id_empleado = 138;

  DBMS_OUTPUT.put_line(v_apellido);
END; 

Primero declaramos una variable local, y haciendo esto introducimos otra característica elegante de PL/SQL: la capacidad de fijar el tipo de datos de nuestra variable en función del tipo de datos de una columna en una tabla (esto será profundizado más adelante en esta serie)

Después ejecutamos una consulta contra la base, obteniendo el apellido del empleado y asignándolo directamente en la variable v_apellido.

Por supuesto, querremos hacer más que ejecutar sentencias SELECT en PL/SQL, también querremos insertar, modificar y eliminar datos desde PL/SQL. Aquí hay ejemplos de cada uno de esos tipos de sentencias DML:

• Eliminamos todos los empleados en el departamento 10 y mostramos cuántas tuplas fueron eliminadas:

DECLARE
  v_id_departamento empleados.id_departamento%TYPE := 10;
BEGIN
  DELETE FROM empleados
       WHERE id_departamento = v_id_departamento;

  DBMS_OUTPUT.put_line(SQL%ROWCOUNT);
END;

Referenciamos la variable PL/SQL directamente dentro de la sentencia DELETE. Cuando el bloque se ejecuta, la variable se reemplaza con el valor actual, 10, y el DELETE es ejecutado por el motor de SQL. SQL%ROWCOUNT es un atributo especial de cursor que retorna el número de tuplas modificadas por la última sentencia DML ejecutada en nuestra sesión.

• Modificar todos los empleados en el departamento 10 con un 20% de incremento salarial.

DECLARE
  v_id_departamento empleados.id_departamento%TYPE := 10;
BEGIN
  UPDATE empleados
    SET salario = salario * 1.2
   WHERE id_departamento = v_id_departamento;

  DBMS_OUTPUT.put_line(SQL%ROWCOUNT);
END;

Insertar un nuevo empleado en la tabla.

BEGIN
  INSERT INTO empleados (id_empleado
                       , apellido
                       , id_departamento
                       , salario)
       VALUES (100
             , 'Feuerstein'
             , 10
             , 200000);

  DBMS_OUTPUT.put_line(SQL%ROWCOUNT);
END;

En este bloque, proveímos los valores de las columnas como literales, en lugar de variables, directamente dentro de la sentencia SQL.

Recursos para Desarrolladores PL/SQL

Si recién estás comenzando con PL/SQL, deberías conocer y aprovechar la multitud de recursos gratuitos online que existen. Aquí hay algunos de los más populares y útiles:

  • La página de PL/SQL en OTN (Oracle Technology Network), en oracle.com/technetwork/database/features/plsql: un excelente punto de comienzo para explorar recursos de PL/SQL en el popular sitio de OTN
  • PL/SQL Obsession, en ToadWorld.com/SF: una colección de recursos de desarrollo PL/SQL del autor, incluyendo material de entrenamiento, presentaciones, código ejemplo, estándares, videos de buenas prácticas, y más
  • PL/SQL Challenge, en plsqlchallenge.com: una pregunta diaria sobre PL/SQL que te ayudará a evaluar y mejorar tu conocimiento sobre PL/SQL

A continuación: Controlando la ejecución en el bloque

En este artículo, has aprendido cómo se enmarca PL/SQL en el amplio mundo de la base de datos Oracle. Has aprendido también cómo definir bloques de código PL/SQL y a nombrar esos bloques, de forma que el código de tu aplicación puede ser utilizado y mantenido con más facilidad. Finalmente, fuiste introducido en la ejecución de sentencias SQL dentro de PL/SQL.

¿Por qué anidar bloques?

Podemos poner BEGIN antes de cualquier conjunto de una o más sentencias ejecutables seguidas de un END, creando un bloque anidado con esas sentencias. Hay dos ventajas principales para hacer esto: (1) posponer la asignación de memoria para variables que se necesitan únicamente en el bloque anidado, y (2) limitar la propagación de una excepción lanzada por una de las sentencias del bloque anidado.

Consideremos el siguiente bloque:

DECLARE
  l_mensaje1  VARCHAR2(100) := 'Hola';
  l_mensaje2  VARCHAR2(100) := ' Mundo!';
BEGIN
  IF SYSDATE >= TO_DATE('01-JAN-2012')
  THEN
    l_mensaje2 := l_mensaje1 || l_mensaje2;
    DBMS_OUTPUT.put_line(l_mensaje2);
  ELSE
    DBMS_OUTPUT.put_line(l_mensaje1);
  END IF;
END;
 

El bloque despliega “¡Hola Mundo!” cuando la fecha de hoy (retornada por SYSDATE) es por lo menos el primer día de 2012; en otro caso, únicamente despliega el mensaje “Hola”. Aunque este bloque se ejecute en 2011, asigna memoria para la variable l_mensaje2.

Si reestructuramos este bloque, la memoria para l_mensaje2 será asignada únicamente después del 2011:

DECLARE
   l_mensaje1  VARCHAR2(100) := 'Hola';
BEGIN
   IF SYSDATE > TO_DATE('01-JAN-2011')
   THEN
      DECLARE
         l_mensaje2  VARCHAR2(100) := ' Mundo!';
      BEGIN
         l_mensaje2 := l_mensaje1 || l_mensaje2;
         DBMS_OUTPUT.put_line(l_mensaje2);
      END;
   ELSE
      DBMS_OUTPUT.put_line(l_mensaje1);
   END IF;
END;
 

De forma similar, podemos agregar una sección de excepciones a este bloque anidado, atrapando errores y permitiendo que el bloque exterior continúe con su ejecución:

DECLARE
   l_mensaje1  VARCHAR2(100) := 'Hola';
BEGIN
   DECLARE
      l_mensaje2  VARCHAR2(5);
   BEGIN
      l_mensaje2 := ' Mundo!';
      DBMS_OUTPUT.put_line(l_mensaje1 || l_mensaje2);
   EXCEPTION
      WHEN OTHERS
      THEN
         DBMS_OUTPUT.put_line(DBMS_UTILITY.format_error_stack);
   END;
   DBMS_OUTPUT.put_line(l_mensaje1);
END;

En este caso, el bloque anidado lanzará una excepción VALUE_ERROR, porque la variable l_mensaje2 es muy pequeña (máximo de 5 bytes) para el texto “ Mundo!”.La sección de excepciones del bloque anidado atrapará y desplegará el error. El bloque exterior continuará su ejecución.

Un posterior artículo en estas series se enfocará en cómo funciona el manejo de excepciones en PL/SQL.

Más adelante en esta serie de artículos, mostraré cómo controlar el flujo de ejecución en nuestros bloques: lógica condicional con IF y CASE; lógica iterativa con FOR, WHILE y LOOP; así como lanzar y manejar excepciones.




Steven Feuerstein es el evangelista en PL/SQL de Quest Software. Ha publicado 10 libros sobre este lenguaje de programación de Oracle, incluyendo Oracle PL/SQL Programming y Oracle PL/SQL Best Practices (O’Reilly Media). Alfonso Vicente es Co-fundador de Logos Consulting y del Grupo de Usuarios Oracle del Uruguay. Trabaja con tecnologías Oracle desde 2004.