Sie sind auf Seite 1von 40

Construyendo con Bloques en PL/SQL

Por Steven Feuerstein


Traducido por Alfonso Vicente
Publicado en marzo 2012
Publicado en

Marzo/Abril 2011

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.

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.
TEMA 12
GENERALIDADES DE PL/SQL
PL/SQL
Generalidades de PL/SQL (I)

 Comentarios en PL/SQL:

o Anteponga a los comentarios de una sola línea dos guiones (--) a modo
de prefijo.
o Coloque comentarios de varias líneas entre /* y */.
 Operadores de PL/SQL:

**, NOT

+, -

*, /

+, -, ||

=, !=, <, >, <=, >=, IS NULL, LIKE, BETWEEN, IN

AND

OR
PL/SQL
Generalidades de PL/SQL (II)

 Bloques Anidados y Ámbito de la Variable:

PL/SQL
Generalidades de PL/SQL (III)

 Para hacer referencia a una variable de sustitución en PL/SQL debe anteponer a su


nombre dos puntos (:) a modo de prefijo:

PL/SQL
Generalidades de PL/SQL (IV)

 Directrices de Programación para facilitar el mantenimiento del código:

o Documente el código con comentarios.


o Desarrolle una convención de mayúsculas/minúsculas para el código.
o Desarrolle convenciones de nomenclatura para identificadores y otros
objetos.
o Sangre el código para facilitar la lectura.
o Evite la ambigüedad entre variables locales, parámetros formales y
nombres de columnas de las tablas de la B.D.
PL/SQL
Generalidades de PL/SQL (V)

 Cuando trabaje con valores nulos puede evitar algunos de los errores más
habituales si recuerda las siguientes reglas:
o Las comparaciones en las que se incluyen valores nulos siempre
resultan NULL.
o Si se aplica el operador lógico NOT a un valor nulo resulta NULL.
o En las sentencias de control condicionales, si la condición resulta NULL,
no se ejecutarán las sentencias asociadas.

PL/SQL
Sentencias SQL en PL/SQL

 SELECT recupera exactamente UNA fila.

 INSERT añade una fila.

 UPDATE modifica una o más filas existentes.

 DELETE suprime una o más filas existentes.

 COMMIT hace permanentes todas las modificaciones pendientes.

 ROLLBACK elimina todas las modificaciones pendientes.

 SAVEPOINT marca un punto intermedio en el procesamiento de las transacciones.

PL/SQL
Sentencia SELECT

 Sintaxis:

 Recuerde, sólo se debe de recuperar una fila. Más de una fila provocará errores.
PL/SQL
Ejemplo de SELECT

 Recupere la suma de los salarios de todos los empleados de un departamento


específico:

PL/SQL
Inserción de Datos

 Añada nueva información sobre los empleados en la tabla emp:

PL/SQL
Actualización de Datos

 Aumente el salario de todos los empleados de la tabla emp que son Analistas:
PL/SQL
Supresión de Datos

 Suprima filas pertenecientes al departamento 10 de la tabla emp:

PL/SQL
Control de Transacciones

 COMMIT finaliza la transacción actual realizando todos los cambios pendientes en


la B.D.

 ROLLBACK finaliza la transacción actual desechando todos los cambios pendientes.

PL/SQL
Control de Transacciones
PL/SQL
Control Flujo de Ejecución

 Puede modificar el flujo lógico de sentencias utilizando sentencias IF condicionales


y estructuras de control de bucles.
 Sentencias IF condicionales:

o IF-THEN
o IF-THEN-ELSE
o IF-THEN-ELSIF
 Control de bucles:

o Bucle básico LOOP


o Bucle FOR
o Bucle WHILE

PL/SQL
Sentencia IF

 Sintaxis:
PL/SQL
Flujo de IF-THEN-ELSE

PL/SQL
Flujo de IF-THEN-ELSIF
PL/SQL
Condiciones Booleanas

PL/SQL
Bucle Básico: LOOP

 Sintaxis:

 Donde condición es una expresión o variable booleana (TRUE, FALSE o NULL).


PL/SQL
Bucle FOR

 Sintaxis:

 No declare el índice; se declara implícitamente como un BINARY_INTEGER. Fuera


del bucle el índice no está definido.
 Los límites desde..hasta deben de ser literales numéricos. Pueden ser expresiones
que se convierten en valores numéricos.

PL/SQL
Bucle WHILE

 Sintaxis:

 La condición se evalúa al inicio de cada iteración

PL/SQL
Etiquetas y Loops Anidados

 Puede anidar bucles a varios niveles.

 Utilice etiquetas para distinguir entre los bloques y los bucles.

 Salga al bucle externo con la sentencia EXIT que hace referencia a la etiqueta.

 Los nombres de etiquetas deben ir antes de la palabra LOOP y entre


los delimitadores << >>.
PL/SQL
Etiquetas y Loops Anidados

 Ejemplo:

TEMA 11
PL/SQL

PL/SQL
¿Qué es PL/SQL?

 Lenguaje de procesamiento procedimental.


 Implementado por Oracle.
 Dispone de estructuras de programación similares a las de la
mayoría de los lenguajes de programación.
 Objetivo: Interactuar con la B.D.

PL/SQL
Estructura Bloques Anónimos

 [DECLARE]
Define objetos PL/SQL que serán utilizados
dentro del mismo bloque
 BEGIN
Sentencias Ejecutables
 [EXCEPTION]
Qué hacer si la acción ejecutada causa error
 END;

PL/SQL
Estructura de Procedimiento

 PROCEDURE nombre IS
Sección Declarativa
 BEGIN
Sección Ejecutable
 [EXCEPTION]
Sección de Excepciones
 END;

PL/SQL
Estructura de Función

 FUNCTION nombre RETURN tipo_dato IS


Sección Declarativa
 BEGIN
Sección Ejecutable
 [EXCEPTION]
Sección de Excepciones
 END;

PL/SQL
Declaración de Variables

 Reglas para nombres:


o Dos variables pueden tener el mismo nombre, si están en
bloques diferentes.
o El nombre de la variable (identificador) no debería ser el
mismo que el de una columna de una tabla utilizada en el
bloque.

PL/SQL
Asignación e Inicialización de Variables

 Asignación:

 Inicialización:

 Por defecto, todas las variables se inicializan a NULL.

PL/SQL
Tipos de Variables

 ESCALARES
 BOOLEANAS
 COMPUESTAS
 LOB
 DE ENLACE (BIND)

PL/SQL
Variables Escalares

 VARCHAR2 (longitud_máxima)
 NUMBER [(precisión, escala)]
 DATE
 CHAR [(longitud_máxima)]
 LONG
 LONG RAW
 BOOLEAN
 BINARY_INTEGER

PL/SQL
Atributo %TYPE

 Permite declarar una variable basada en:


o Otras variables previamente declaradas
o La definición de una columna de la base de datos
 Preceder de %TYPE por:
o La tabla y la columna de la base de datos
o El nombre de la variable definida con anterioridad
 Ejemplo:
v_ename
emp.ename%TYPE;

PL/SQL
Variables BOOLEANAS

 A una variable Boolean sólo se le pueden asignar los


valores: TRUE, FALSE o NULL.
 Estas variables están conectadas por los operadores lógicos AND,
OR y NOT.

PL/SQL
Tipos de Datos Compuestos

 Tipos:
o REGISTROS PL/SQL
o TABLAS PL/SQL
 Contienen componentes internos
PL/SQL
Creación de un Registro

 Sintaxis:

 Donde declaración_campo significa:

PL/SQL
Ejemplo de Registro PL/SQL

 Declarar un registro para almacenar el número de empleado,


nombre, trabajo y sueldo de un nuevo empleado:

 Los componentes individuales del registro se referenciarán de


forma cualificada; en este ejemplo:

PL/SQL
El Atributo %ROWTYPE
 Define un registro con la estructura de la tabla o vista de la B.D.
 Los campos del registro toman sus nombres y tipos de datos de las
columnas de la vista o tabla.
 Ejemplo:
DECLARE registro_empleado emp%ROWTYPE;

PL/SQL
Ventajas de %ROWTYPE

 El número y los tipos de datos de las columnas de la tabla pueden


no ser conocidos.
 Simplifica la programación al no tener que definir explícitamente los
campos y tipos del registro.
 Es útil para realizar recuperaciones de filas con la sentencia
SELECT:

PL/SQL
Tablas PL/SQL

 Cuentan con dos componentes:


o TIPO DE DATOS DE CLAVE PRIMARIA BINARY_INTEGER
o COLUMNA DE TIPO DE DATOS ESCALARES O DE
REGISTRO.
 Aumentan dinámicamente porque no tienen restricciones.
 Se almacenan en memoria.

PL/SQL
Creación de Tablas PL/SQL

 Sintaxis:
 Ejemplo:

PL/SQL
Creación de Tablas PL/SQL

PL/SQL
Variables LOB

 Permiten almacenar bloques de datos no estructurados, como


pantallas de texto, imágenes gráficas, vídeo clips, y sonido, de
hasta 4 Gb. de tamaño.

CLOB Character Large Object. Se utiliza para almacenar bloques grandes de datos de caracteres.

BLOB Binary Large Object. Se utiliza para almacenar objetos binarios grandes en la B.D.
BFILE Binary File. Se utiliza para almacenar objetos binarios grandes en archivos del sistema operativo, f

NCLOB National Language Character Large Object. Se utiliza para almacenar en la B.D. bloques grandes d

PL/SQL
Variables de Enlace (Bind)

 Son variables de SQL*Plus.


 Las pueden referenciar bloques de PL/SQL mediante el uso del
ampersand (&).
 Ejemplo:

Descripción
SQL es un lenguaje de consulta, para los sistemas de bases de datos relacionales, que no posee la
potencia de los lenguajes de programación. No permite el uso de variables, estructuras de control de
flujo, bucles y demás elementos característicos de la programación. No es de extrañar, SQL es un
lenguaje de consulta, no un lenguaje de programación.
Sin embargo, SQL es la herramienta ideal para trabajar con bases de datos. Cuando se desea realizar
una aplicación completa, para el manejo de una base de datos relacional, resulta necesario utilizar
alguna herramienta que soporte la capacidad de consulta del SQL y la versatilidad de los lenguajes de
programación tradicionales. PL/SQL es el lenguaje de programación que proporciona Oracle para
extender el SQL estándar con otro tipo de instrucciones y elementos propios de los lenguajes de
programación.
Con PL/SQL vamos a poder programar las unidades de programa de la base de datos Oracle:

 Procedimientos almacenados
 Funciones
 Triggers
 Scripts
Pero además, PL/SQL nos permite realizar programas sobre las siguientes herramientas de Oracle:

 Oracle Forms
 Oracle Reports
 Oracle Graphics
 Oracle Aplication Server
Conceptos básicos
Como introducción vamos a ver algunos elementos y conceptos básicos del lenguaje. PL/SQL no es
CASE-SENSITIVE, es decir, no diferencia mayúsculas de minúsculas como otros lenguajes de
programación como C o Java. Sin embargo, debemos recordar que Oracle es CASE-SENSITIVE en las
búsquedas de texto.
Una línea en PL/SQL contiene grupos de caracteres, conocidos como UNIDADES LÉXICAS, que pueden
ser clasificadas como: DELIMITADORES, IDENTIFICADORES, LITERALES, COMENTARIOS o
EXPRESIONES.

 DELIMITADOR: Es un símbolo, simple o compuesto, que tiene una función especial en PL/SQL:
o Operadores Aritméticos
o Operadores Lógicos
o Operadores Relacionales
 IDENTIFICADOR: Son empleados para nombrar objetos de programas en PL/SQL, así como a
unidades dentro del mismo:
o Constantes
o Cursores
o Variables
o Subprogramas
o Excepciones
o Paquetes
 LITERAL: Es un valor de tipo numérico, carácter, cadena o lógico no representado por un
identificador (es un valor explícito).
 COMENTARIO: Es una aclaración que el programador incluye en el código. Son soportados dos estilos
de comentarios, el de línea simple y de multilínea, para lo cual son empleados ciertos caracteres
especiales.

Tipos de datos
Cada constante y variable tiene un tipo de dato en el que se especifica el formato de almacenamiento,
restricciones y rango de valores válidos. PL/SQL proporciona una variedad predefinida de tipos de
datos. Casi todos los tipos de datos manejados por PL/SQL son similares a los soportados por SQL. A
continuación se muestran los tipos de datos más comunes:

 NUMBER (numérico): Almacena números enteros o de punto flotante, virtualmente de cualquier


longitud, aunque puede ser especificada la precisión (número de dígitos) y la escala, que es la que
determina el número de decimales.
 CHAR (carácter): Almacena datos de tipo carácter con un tamaño máximo de 32.767 bytes y cuyo
valor de longitud por defecto es 1.
 VARCHAR2 (carácter de longitud variable): Almacena datos de tipo carácter empleando sólo la
cantidad necesaria aún cuando la longitud máxima sea mayor.
 BOOLEAN (lógico): Se emplea para almacenar valores TRUE o FALSE.
 DATE (fecha): Almacena datos de tipo fecha. Las fechas se almacenan internamente como datos
numéricos, por lo que es posible realizar operaciones aritméticas con ellas.
 Atributos de tipo. Un atributo de tipo PL/SQL es un modificador que puede ser usado para obtener
información de un objeto de la base de datos. El atributo %TYPE permite conocer el tipo de una
variable, constante o campo de la base de datos. El atributo %ROWTYPE permite obtener los tipos de
todos los campos de una tabla de la base de datos, de una vista o de un cursor.

Tablas de PL/SQL
Las tablas de PL/SQL son tipos de datos que nos permiten almacenar varios valores del mismo tipo de
dato. Una tabla PL/SQL, que es similar a un array, tiene dos componentes:

 Un índice de tipo BINARY_INTEGER que permite acceder a los elementos en la tabla PL/SQL.
 Una columna de escalares o registros que contiene los valores de la tabla PL/SQL.
Puede incrementar su tamaño dinámicamente. La sintaxis general para declarar una tabla de PL es la
siguiente:
TYPE <nombre_tipo_tabla> IS TABLE OF
<tipo_datos>[NOT NULL]
INDEX BY BINARY_INTEGER ;
Una vez que hemos definido el tipo, podemos declarar variables y asignarle valores.
DECLARE /* Definimos el tipo PAISES como tabla PL/SQL */
TYPE PAISES IS TABLE OF NUMBER INDEX BY BINARY_INTEGER ;
/* Declaramos una variable del tipo PAISES */
tPAISES PAISES;
BEGIN
tPAISES(1) := 1;
tPAISES(2) := 2;
tPAISES(3) := 3;
END;
No es posible inicializar las tablas en la declaración. El rango de binary integer es –2147483647..
2147483647, por lo tanto el índice puede ser negativo, lo cual indica que el índice del primer valor no
tiene que ser necesariamente el cero.

Tablas PL/SQL de registros


Es posible declarar elementos de una tabla PL/SQL como de tipo registro.
DECLARE
TYPE PAIS IS RECORD (
CO_PAIS
NUMBER NOT NULL ,
DESCRIPCION VARCHAR2(50),
CONTINENTE VARCHAR2(20) );
TYPE PAISES IS TABLE OF PAIS INDEX BY BINARY_INTEGER ;
tPAISES PAISES;
BEGIN
tPAISES(1).CO_PAIS := 27;
tPAISES(1).DESCRIPCION := 'ITALIA';
tPAISES(1).CONTINENTE := 'EUROPA';
END;
Cuando trabajamos con tablas de PL podemos utilizar las siguientes funciones:

 FIRST. Devuelve el menor índice de la tabla. NULL si está vacía.


 LAST. Devuelve el mayor índice de la tabla. NULL si está vacía.
 EXISTS(i). Utilizada para saber si en un cierto índice hay almacenado un valor. Devolverá TRUE si en
el índice i hay un valor.
 COUNT. Devuelve el número de elementos de la tabla PL/SQL.
 PRIOR (n). Devuelve el número del índice anterior a n en la tabla.
 NEXT(n). Devuelve el número del índice posterior a n en la tabla.
 TRIM. Borra un elemento del final de la tabla PL/SQL.
 TRIM(n). Borra n elementos del final de la tabla PL/SQL.
 DELETE. Borra todos los elementos de la tabla PL/SQL.
 DELETE(n) Borra el correspondiente al índice n.
 DELETE(m,n) Borra los elementos entre m y n.

Estructuras
Estas son las diversas estructuras que se manejan en PL/SQL:

Estructuras de control de flujo


En PL/SQL sólo disponemos de la estructura condicional IF. Su sintaxis se muestra a continuación:
IF (expresión) THEN
-- Instrucciones
ELSIF (expresión) THEN
-- Instrucciones
ELSE
-- Instrucciones
END IF;
Un aspecto a tener en cuenta es que la instrucción condicional anidada es ELSIF y no "ELSEIF".

Sentencia GOTO
PL/SQL dispone de la sentencia GOTO. La sentencia GOTO desvía el flujo de ejecución a una
determinada etiqueta. En PL/SQL las etiquetas se indican del siguiente modo: << etiqueta >>. El
siguiente ejemplo ilustra el uso de GOTO:
DECLARE
flag NUMBER;
BEGIN
flag :=1 ;
IF (flag = 1) THEN
GOTO paso2;
END IF;
<<paso1>>
dbms_output.put_line('Ejecucion de paso 1');
<<paso2>>
dbms_output.put_line('Ejecucion de paso 2');
END;

Bucles
En PL/SQL tenemos a nuestra disposición los siguientes iteradores o bucles: LOOP, WHILE, FOR. El
bucle LOOP se repite tantas veces como sea necesario hasta que se fuerza su salida con la instrucción
EXIT. Su sintaxis es la siguiente:
LOOP
-- Instrucciones
IF (expresión) THEN
-- Instrucciones
EXIT;
END IF;
END LOOP;
El bucle WHILE, se repite mientras que se cumpla la expresión:
WHILE (expresión) LOOP
-- Instrucciones
END LOOP;
El bucle FOR se repite tanta veces como le indiquemos en los identificadores inicio y final:
FOR contador IN [REVERSE] inicio..final LOOP
-- Instrucciones
END LOOP;
En el caso de especificar REVERSE el bucle se recorre en sentido inverso.

Bloques PL/SQL
Un programa de PL/SQL está compuesto por bloques, como mínimo de uno. Los bloques de PL/SQL
pueden ser de los siguientes tipos:

 Bloques anónimos.
 Subprogramas.
Estructura de un Bloque
Los bloques PL/SQL presentan una estructura específica compuesta de tres partes bien diferenciadas:

 La sección declarativa, donde se declaran todas las constantes y variables que se van a utilizar en la
ejecución del bloque.
 La sección de ejecución, que incluye las instrucciones a ejecutar en el bloque PL/SQL.
 La sección de excepciones, en donde se definen los manejadores de errores que soportará el bloque
PL/SQL.
Cada una de las partes anteriores se delimita por una palabra reservada, de modo que un bloque
PL/SQL se puede representar como sigue:
[declare | is | as ] /*Parte declarativa*/
begin /*Parte de ejecucion*/
[ exception ] /*Parte de excepciones*/
end;
De las anteriores partes, únicamente la sección de ejecución es obligatoria, que quedaría delimitada
entre las cláusulas BEGIN y END. Veamos un ejemplo de bloque PL/SQL muy genérico. Se trata de un
bloque anónimo, es decir, no lo identifica ningún nombre. Los bloques anónimos identifican su parte
declarativa con la palabra reservada DECLARE.
DECLARE
/*Parte declarativa*/
nombre_variable DATE;
BEGIN
/*Parte de ejecución
* Este código asigna el valor de la columna "nombre_columna"
* a la variable identificada por "nombre_variable"
*/
SELECT SYSDATE INTO nombre_variable FROM DUAL;
EXCEPTION
/*Parte de excepciones*/
WHEN OTHERS THEN
dbms_output.put_line('Se ha producido un error');
END;

Cursores en PL/SQL
PL/SQL utiliza cursores para gestionar las instrucciones SELECT. Un cursor es un conjunto de registros
devuelto por una instrucción SQL. Técnicamente, los cursores son fragmentos de memoria reservados
para procesar los resultados de una consulta SELECT. Podemos distinguir dos tipos de cursores:

 Cursores implícitos. Este tipo de cursores se utiliza para operaciones SELECT INTO. Se usan cuando la
consulta devuelve un único registro.
 Cursores explícitos. Son los cursores que son declarados y controlados por el programador. Se utilizan
cuando la consulta devuelve un conjunto de registros. Ocasionalmente también se utilizan en
consultas que devuelven un único registro por razones de eficiencia. Son más rápidos.
Un cursor se define como cualquier otra variable de PL/SQL y debe nombrarse de acuerdo a los
mismos convenios que cualquier otra variable. Los cursores implícitos no necesitan declaración. El
siguiente ejemplo declara un cursor explícito:
declare
cursor c_paises is
SELECT CO_PAIS, DESCRIPCION
FROM PAISES;
begin
/* Sentencias del bloque ...*/
end;
Para procesar instrucciones SELECT que devuelvan más de una fila, son necesarios cursores explícitos
combinados con un estructura de bloque. Un cursor admite el uso de parámetros. Los parámetros
deben declararse junto con el cursor. El siguiente ejemplo muestra la declaración de un cursor con un
parámetro, identificado por p_continente.
declare
cursor c_paises (p_continente IN VARCHAR2) is
SELECT CO_PAIS, DESCRIPCION
FROM PAISES
WHERE CONTINENTE = p_continente;
begin
/* Sentencias del bloque ...*/
end;

Cursores implícitos
Los cursores implícitos se utilizan para realizar consultas SELECT que devuelven un único registro.
Deben tenerse en cuenta los siguientes puntos cuando se utilizan cursores implícitos:

 Con cada cursor implícito debe existir la palabra clave INTO.


 Las variables que reciben los datos devueltos por el cursor tienen que contener el mismo tipo de dato
que las columnas de la tabla.
 Los cursores implícitos solo pueden devolver una única fila. En caso de que se devuelva más de una
fila (o ninguna fila) se producirá una excepción.

Excepciones asociadas a los cursores implícitos


Los cursores implícitos sólo pueden devolver una fila, por lo que pueden producirse determinadas
excepciones. Las más comunes que se pueden encontrar son no_data_found y too_many_rows. La
siguiente tabla explica brevemente estas excepciones:

Excepción Explicación

Se produce cuando una sentencia SELECT intenta recuperar datos pero


NO_DATA_FOUND
ninguna fila satisface sus condiciones. Es decir, cuando "no hay datos".

Dado que cada cursor implícito sólo es capaz de recuperar una fila , esta
TOO_MANY_ROWS
excepción detecta la existencia de más de una fila.

Cursores explícitos
Los cursores explícitos se emplean para realizar consultas SELECT que pueden devolver cero filas o
más de una fila. Para trabajar con un cursor explícito necesitamos realizar las siguientes tareas:

 Declarar el cursor.
 Abrir el cursor con la instrucción OPEN.
 Leer los datos del cursor con la instrucción FETCH.
 Cerrar el cursor y liberar los recursos con la instrucción CLOSE
A continuación se muestra un ejemplo del empleo de cursores:
CURSOR nombre_cursor IS
instrucción_SELECT // Se declara el cursor

CURSOR nombre_cursor(param1 tipo1, ..., paramN tipoN) IS


instrucción_SELECT // Declaracion de cursor con parametro

OPEN nombre_cursor; // Abrir un cursor

OPEN nombre_cursor(valor1, valor2, ..., valorN); // abrir cursor con parametros


FETCH nombre_cursor INTO lista_variables; // Recuperada datos en variables

FETCH nombre_cursor INTO registro_PL/SQL; // Recupera datos en registro

CLOSE nombre_cursor; // Cierra el cursor


El siguiente ejemplo ilustra el trabajo con un cursor explícito. Hay que tener en cuenta que, al leer los
datos del cursor, debemos hacerlo sobre variables del mismo tipo de datos de la tabla (o tablas) que
trata el cursor.
DECLARE
CURSOR cpaises
IS SELECT CO_PAIS, DESCRIPCION, CONTINENTE
FROM PAISES;
co_pais VARCHAR2(3);
descripcion VARCHAR2(50);
continente VARCHAR2(25);
BEGIN
OPEN cpaises;
FETCH cpaises INTO co_pais,descripcion,continente;
CLOSE cpaises;
END;

Manejo de excepciones
En PL/SQL una advertencia o condición de error se llama excepción. Las excepciones se controlan
dentro de su propio bloque, cuya estructura se muestra a continuación:
DECLARE
-- Declaraciones
BEGIN
-- Ejecución
EXCEPTION
-- Excepción
END;
Cuando ocurre un error, se ejecuta la porción del programa marcada por el bloque EXCEPTION,
transfiriéndose el control a ese bloque de sentencias. El siguiente ejemplo muestra un bloque de
excepciones que captura las excepciones NO_DATA_FOUND y ZERO_DIVIDE. Cualquier otra excepción
será capturada en el bloque WHEN OTHERS THEN.
DECLARE
-- Declaraciones
BEGIN
-- Ejecucion
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- Se ejecuta cuando ocurre una excepción de tipo NO_DATA_FOUND
WHEN ZERO_DIVIDE THEN
-- Se ejecuta cuando ocurre una excepción de tipo ZERO_DIVIDE
WHEN OTHERS THEN
-- Se ejecuta cuando ocurre una excepción de un tipo no tratado
-- en los bloques anteriores
END;
Como se ha indicado, cuando ocurre un error se ejecuta el bloque EXCEPTION, transfiriéndose el
control a las sentencias del bloque. Una vez finalizada la ejecución del bloque de EXCEPTION no se
continúa ejecutando el bloque anterior. Si existe un bloque de excepción apropiado para el tipo de
excepción se ejecuta dicho bloque. Si no existe un bloque de control de excepciones adecuado al tipo
de excepción se ejecutará el bloque de excepción WHEN OTHERS THEN (¡si existe!). WHEN OTHERS
debe ser el último manejador de excepciones.
Las excepciones pueden ser definidas de forma interna o explícitamente por el usuario. Ejemplos de
excepciones definidas de forma interna son la división por cero y la falta de memoria en tiempo de
ejecución. Estas mismas condiciones excepcionales tienen sus propio tipos y pueden ser referenciadas
por ellos: ZERO_DIVIDE y STORAGE_ERROR. Las excepciones definidas por el usuario deben ser
alcanzadas explícitamente utilizando la sentencia RAISE.
Con las excepciones se pueden manejar los errores cómodamente sin necesidad de mantener
múltiples chequeos por cada sentencia escrita. También provee claridad en el código ya que permite
mantener las rutinas correspondientes al tratamiento de los errores de forma separada de la lógica del
negocio.

Excepciones predefinidas
PL/SQL proporciona un gran número de excepciones predefinidas que permiten controlar las
condiciones de error más habituales. Las excepciones predefinidas no necesitan ser declaradas,
simplemente se utilizan cuando éstas son lanzadas por algún error determinado. La siguiente lista
muestra las excepciones predeterminadas por PL/SQL y una breve descripción de cuándo son
accionadas:

Excepción Descripción

El programa intentó asignar valores a los atributos de un objet


ACCESS_INTO_NULL
inicializado -6530.

El programa intentó asignar valores a una tabla anidada aún n


COLLECTION_IS_NULL
inicializada -6531.

El programa intentó abrir un cursor que ya se encontraba abie


CURSOR_ALREADY_OPEN Recuerde que un cursor de ciclo FOR automáticamente lo abre
no se debe especificar con la sentencia OPEN -6511.

El programa intentó almacenar valores duplicados en una colu


DUP_VAL_ON_INDEX que se mantiene con restricción de integridad de un índice úni
(unique index) -1.

El programa intentó efectuar una operación no válida sobre un


INVALID_CURSOR
cursor -1001.

En una sentencia SQL, la conversión de una cadena de caracte


INVALID_NUMBER hacia un número falla cuando esa cadena no representa un nú
válido -1722.

El programa intentó conectarse a Oracle con un nombre de us


LOGIN_DENIED
o password inválido -1017.

Una sentencia SELECT INTO no devolvió valores o el programa


NO_DATA_FOUND
referenció un elemento no inicializado en una tabla indexada 1
Excepción Descripción

El programa efectuó una llamada a Oracle sin estar conectado


NOT_LOGGED_ON
1012.

PROGRAM_ERROR PL/SQL Tiene un problema interno -6501.

Los elementos de una asignación (el valor a asignar y la variab


que lo contendrá) tienen tipos incompatibles. También se pres
ROWTYPE_MISMATCH
este error cuando un parámetro pasado a un subprograma no
tipo esperado -6504.

El parámetro SELF (el primero que es pasado a un método


SELF_IS_NULL
MEMBER) es nulo -30625.

STORAGE_ERROR La memoria se terminó o está corrupta -6500.

El programa está tratando de referenciar un elemento de una


SUBSCRIPT_BEYOND_COUNT colección indexada que se encuentra en una posición más gran
que el número real de elementos de la colección -6533.

El programa está referenciando un elemento de una tabla utiliz


SUBSCRIPT_OUTSIDE_LIMIT un número fuera del rango permitido (por ejemplo, el element
1”) -6532.

La conversión de una cadena de caracteres hacia un tipo ROW


SYS_INVALID_ROWID
falló porque la cadena no representa un número -1410.

Se excedió el tiempo máximo de espera por un recurso en Ora


TIMEOUT_ON_RESOURCE
51.

TOO_MANY_ROWS Una sentencia SELECT INTO devuelve más de una fila -1422.

Ocurrió un error aritmético, de conversión o truncamiento. Por


VALUE_ERROR ejemplo, sucede cuando se intenta introducir un valor muy gra
dentro de una variable más pequeña -6502.

ZERO_DIVIDE El programa intentó efectuar una división por cero -1476.


Excepciones definidas por el usuario
PL/SQL permite al usuario definir sus propias excepciones, que deberán ser declaradas y lanzadas
explícitamente utilizando la sentencia RAISE. Las excepciones deben ser declaradas en el segmento
DECLARE de un bloque, subprograma o paquete. Se declara una excepción como cualquier otra
variable, asignándole el tipo EXCEPTION. Las mismas reglas de alcance aplican tanto sobre variables
como sobre las excepciones.
DECLARE
-- Declaraciones
MyExcepcion EXCEPTION;
BEGIN -- Ejecución
EXCEPTION -- Excepción
END;

Reglas de alcance
Una excepción es válida dentro de su ambito de alcance, es decir, el bloque o programa donde ha sido
declarada. Las excepciones predefinidas son siempre válidas. Como las variables, una excepción
declarada en un bloque es local a ese bloque y global a todos los subbloques que comprende.

La sentencia RAISE
La sentencia RAISE permite lanzar una excepción en forma explícita. Es posible utilizar esta sentencia
en cualquier lugar que se encuentre dentro del alcance de la excepción.
DECLARE
-- Declaramos una excepción identificada por VALOR_NEGATIVO
VALOR_NEGATIVO EXCEPTION;
valor NUMBER;
BEGIN
-- Ejecución valor := -1;
IF valor < 0 THEN
RAISE VALOR_NEGATIVO;
END IF;
EXCEPTION -- Excepción
WHEN VALOR_NEGATIVO THEN
dbms_output.put_line('El valor no puede ser negativo');
END;
Con la sentencia RAISE podemos lanzar una excepción definida por el usuario o predefinida, siendo el
comportamiento habitual lanzar excepciones definidas por el usuario.

Propagación de excepciones
Una de las características más interesantes de la excepciones es su propagación. Cuando se lanza una
excepción, el control se transfiere hasta la sección EXCEPTION del bloque donde se ha producido la
excepción. Entonces se busca un manejador válido de la excepción (WHEN THEN, WHEN OTHERS
THEN) dentro del bloque actual. En el caso de que no se encuentre ningún manejador válido, el
control del programa se desplaza hasta el bloque EXCEPTION del bloque que ha realizado la llamada
PL/SQL.

Subprogramas en PL/SQL
Como hemos visto anteriormente, los bloques de PL/SQL pueden ser bloques anónimos (scripts) y
subprogramas. Los subprogramas son bloques de PL/SQL a los que asignamos un nombre
identificativo y que normalmente almacenamos en la propia base de datos para su posterior ejecución.
Los subprogramas pueden recibir parámetros. Los subprogramas pueden ser de varios tipos:

 Procedimientos almacenados.
 Funciones.
 Triggers.
 Subprogramas en bloques anónimos.
Triggers
Un trigger es un bloque PL/SQL asociado a una tabla, que se ejecuta como consecuencia de una
determinada instrucción SQL (una operación DML: INSERT, UPDATE o DELETE) sobre dicha tabla. La
sintaxis para crear un trigger es la siguiente:
CREATE [OR REPLACE] TRIGGER <nombre_trigger>
{BEFORE|AFTER}
{DELETE|INSERT|UPDATE [OF col1, col2, ..., colN]
[OR {DELETE|INSERT|UPDATE [OF col1, col2, ..., colN]...]}
ON <nombre_tabla>
[FOR EACH ROW [WHEN (<condicion>)]]
DECLARE
-- variables locales
BEGIN
-- Sentencias
[EXCEPTION]
-- Sentencias control de excepcion
END <nombre_trigger>;
El uso de OR REPLACE permite sobreescribir un trigger existente. Si se omite y el trigger existe se
producirá un error. Los triggers pueden definirse para las operaciones INSERT, UPDATE o DELETE, y
pueden ejecutarse antes o después de la operación. El modificador BEFORE AFTER indica que el
trigger se ejecutará antes o después de ejecutarse la sentencia SQL definida por DELETE, INSERT,
UPDATE. Si incluimos el modificador OF el trigger, solo se ejecutará cuando la sentencia SQL afecte a
los campos incluidos en la lista.
El alcance de los disparadores puede ser la fila o de orden. El modificador FOR EACH ROW indica que
el trigger se disparará cada vez que se realicen operaciones sobre una fila de la tabla. Si se acompaña
del modificador WHEN, se establece una restricción; el trigger solo actuará, sobre las filas que
satisfagan la restricción. La siguiente tabla resume los contenidos anteriores.

Valor Descripción

INSERT,
DELETE, Define qué tipo de orden DML provoca la activación del disparador.
UPDATE

BEFORE,
Define si el disparador se activa antes o después de que se ejecute la orden.
AFTER

Los disparadores con nivel de fila se activan una vez por cada fila afectada por
FOR EACH orden que provocó el disparo. Los disparadores con nivel de orden se activan s
ROW una vez, antes o después de la orden. Los disparadores con nivel de fila se
identifican por la cláusula FOR EACH ROW en la definición del disparador
La cláusula WHEN sólo es válida para los disparadores con nivel de fila.

Procedimiento
Un procedimiento es un subprograma que ejecuta una acción especifica y que no devuelve ningún
valor. Un procedimiento tiene un nombre, un conjunto de parámetros (opcional) y un bloque de
código. La sintaxis de un procedimiento almacenado es la siguiente:
CREATE [OR REPLACE]
PROCEDURE <procedure_name> [(<param1> [IN|OUT|IN OUT] <type>,
<param2> [IN|OUT|IN OUT] <type>, ...)]
IS
-- Declaración de variables locales
BEGIN
-- Sentencias
[EXCEPTION]
-- Sentencias control de excepción
END [<procedure_name>];
El uso de OR REPLACE permite sobreescribir un procedimiento existente. Si se omite, y el
procedimiento existe, se producirá un error. La sintaxis es muy parecida a la de un bloque anónimo,
salvo porque se reemplaza la sección DECLARE por la secuencia PROCEDURE … IS y en la
especificación del procedimiento debemos especificar el tipo de datos de cada parámetro. Al
especificar el tipo de dato del parámetro no debemos especificar la longitud del tipo. Los parámetros
pueden ser de entrada (IN), de salida (OUT) o de entrada salida (IN OUT). El valor por defecto es IN,
y se toma ese valor en caso de que no especifiquemos nada.
CREATE OR REPLACE
PROCEDURE Actualiza_Saldo(cuenta NUMBER,
new_saldo NUMBER)
IS
-- Declaración de variables locales
BEGIN
-- Sentencias
UPDATE SALDOS_CUENTAS
SET SALDO = new_saldo,
FX_ACTUALIZACION = SYSDATE
WHERE CO_CUENTA = cuenta;
END Actualiza_Saldo;

Función
Una función es un subprograma que devuelve un valor. La sintaxis para construir funciones es la
siguiente:
CREATE [OR REPLACE]
FUNCTION <fn_name>[(<param1> IN <type>, <param2> IN <type>, ...)]
RETURN
<return_type>
IS
result <return_type>;
BEGIN return(result);
[EXCEPTION]
-- Sentencias control de excepción
END [<fn_name>];
El uso de OR REPLACE permite sobreescribir una función existente. Si se omite, y la función existe, se
producirá un error. La sintaxis de los parámetros es la misma que en los procedimientos almacenados,
exceptuando que sólo pueden ser de entrada.

Das könnte Ihnen auch gefallen