PL SQL
PL SQL
Operadores
Operador de
asignacin
Operadores
aritmticos
+ (suma)
- (resta)
* (multiplicacin)
/ (divisin)
** (exponente)
Operadores
relacionales o de
comparacin
= (igual a)
<> (distinto de)
< (menor que)
> (mayor que)
>= (mayor o igual a)
<= (menor o igual a)
Operadores lgicos
AND (y lgico)
NOT (negacion)
OR (o lgico)
Operador de
concatenacin
||
IF (expresion) THEN
-- Instrucciones
ELSIF (expresion) THEN
-- Instrucciones
ELSE
-- Instrucciones
END IF;
Un aspecto a tener en cuenta es que la instruccin condicional anidada
es ELSIF y no "ELSEIF".
Sentencia GOTO
PL/SQL dispone de la sentencia GOTO. La sentencia GOTO desvia el flujo de
ejecuci 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 disposicin 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 instruccin EXIT. Su sintaxis es la siguiente
LOOP
-- Instrucciones
IF (expresion) THEN
-- Instrucciones
EXIT;
END IF;
END LOOP;
Bloques PL/SQL
Bloques annimos
Subprogramas
Estructura de un Bloque
Los bloques PL/SQL presentan una estructura especfica compuesta de tres
partes bien diferenciadas:
[ declare | is | as ]
/*Parte declarativa*/
begin
/*Parte de ejecucion*/
[ exception ]
/*Parte de excepciones*/
end;
De las anteriores partes, nicamente la seccin de ejecucin es obligatoria, que
quedara delimitada entre las clusulas BEGIN yEND. Veamos un ejemplo de
bloque PL/SQL muy genrico. Se trata de un bloque annimos, es decir no lo
identifica ningn nombre. Los bloques annimos identifican su parte declarativa con
la palabra reservada DECLARE.
DECLARE
/*Parte declarativa*/
nombre_variable DATE;
BEGIN
/*Parte de ejecucion
* Este cdigo asigna el valor de la columna "nombre_columna"
* a la variable identificada por "nombre_variable"
*/
SELECT SYSDATE
INTO nombre_variable
FROM DUAL;
EXCEPTION
END;
/*Parte de excepciones*/
WHEN OTHERS THEN
dbms_output.put_line('Se ha producido un error');
La clusula NOT NULL impide que a una variable se le asigne el valor nulo, y
por tanto debe inicializarse a un valor diferente de NULL.
Los tipos escalares incluyen los definidos en SQL ms los tipos VARCHAR y
BOOLEAN. Este ltimo puede tomar los valores TRUE, FALSE y NULL, y se suele
utilizar para almacenar el resultado de alguna operacin lgica. VARCHAR es un
sinnimo de CHAR.
Ejemplos:
Estructura de un bloque annimo.
DECLARE
/* Se declara la variable de tipo VARCHAR2(15) identificada por v_location y se
le asigna
el valor "Granada"*/
v_location VARCHAR2(15) := Granada;
asigna
/*Se declara la variable del mismo tipo que tenga el campo nombre de la
tabla tabla_empleados
identificada por v_nombre y no se le asigna ningn valor */
v_nombre tabla_empleados.nombre%TYPE;
/*Se declara la variable del tipo registro correspondiente a un supuesto
cursor, llamado
micursor, identificada por reg_datos*/
reg_datos micursor%ROWTYPE;
BEGIN
/*Parte de ejecucion*/
EXCEPTION
/*Parte de excepciones*/
END;
Estructura de un subprograma:
asigna
/*Se declara la variable del mismo tipo que tenga el campo nombre de la
tabla tabla_empleados
identificada por v_nombre y no se le asigna ningn valor */
v_nombre tabla_empleados.nombre%TYPE;
/*Se declara la variable del tipo registro correspondiente a un supuesto
cursor, llamado
Cursores en PL/SQL
Introduccin a cursores PL/SQL
PL/SQL utiliza cursores para gestionar las instrucciones SELECT. Un cursor es un
conjunto de registros devuelto por una instruccin SQL. Tcnicamente los cursores
son fragmentos de memoria que reservados para procesar los resultados de una
consulta SELECT.
Podemos distinguir dos tipos de cursores:
Cursores explicitos. Son los cursores que son declarados y controlados por
el programador. Se utilizan cuando la consulta devuelve un conjunto de registros.
Ocasionalmente tambin se utilizan en consultas que devuelven un nico registro
por razones de eficiencia. Son ms rpidos.
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 implicitos
no necesitan declaracin.
El siguiente ejemplo declara un cursor explicito:
declare
cursor c_paises is
SELECT CO_PAIS, DESCRIPCION
FROM PAISES;
begin
/* Sentencias del bloque ...*/
end;
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 Implicitos
Declaracin de cursores implicitos.
Los cursores implicitos se utilizan para realizar consultas SELECT que devuelven
un nico registro.
Deben tenerse en cuenta los siguientes puntos cuando se utilizan cursores
implicitos:
Los cursores implicitos solo pueden devolver una nica fila. En caso de que
se devuelva ms de una fila (o ninguna fila) se producir una excepcion. No se
preocupe si an no sabe que es una excepcion, le valdr conocer que es el medio
por el que PL/SQL gestiona los errores.
El siguiente ejemplo muestra un cursor implicito:
declare
vdescripcion VARCHAR2(50);
begin
SELECT DESCRIPCION
INTO vdescripcion
from PAISES
WHERE CO_PAIS = 'ESP';
dbms_output.put_line('La lectura del cursor es: ' || vdescripcion);
end;
Excepcion
NO_DATA_FOUND
Explicacion
Se produce cuando una sentencia SELECT intenta recuperar datos pero ninguna fila satisface sus
condiciones. Es decir, cuando "no hay datos"
TOO_MANY_ROWS Dado que cada cursor implicito slo es capaz de recuperar una fila , esta excepcion detecta la
existencia de ms de una fila.
Declarar el cursor.
CURSOR nombre_cursor IS
instruccin_SELECT
Tambin debemos declarar los posibles parametros que requiera el cursor:
OPEN nombre_cursor;
o bien (en el caso de un cursor con parmetros)
OPEN nombre_cursor(valor1, valor2, ..., valorN);
Para recuperar los datos en variables PL/SQL.
CLOSE nombre_cursor;
El siguiente ejemplo ilustra el trabajo con un cursor explicito. 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;
Podemos simplificar el ejemplo utilizando el atributo de tipo %ROWTYPE sobre el
cursor.
DECLARE
CURSOR cpaises
IS
SELECT CO_PAIS, DESCRIPCION, CONTINENTE
FROM PAISES;
registro cpaises%ROWTYPE;
BEGIN
OPEN cpaises;
FETCH cpaises INTO registro;
CLOSE cpaises;
END;
El mismo ejemplo, pero utilizando parmetros:
DECLARE
CURSOR cpaises (p_continente VARCHAR2)
IS
SELECT CO_PAIS, DESCRIPCION, CONTINENTE
FROM PAISES
WHERE CONTINENTE = p_continente;
registro cpaises%ROWTYPE;
BEGIN
OPEN cpaises('EUROPA');
FETCH cpaises INTO registro;
CLOSE cpaises;
END;
Cuando trabajamos con cursores debemos considerar:
Atributos de cursores
Atributo
%NOTFOUN
D
%FOUND
%ISOPEN
%ROWCOUN
T
Antes de
abrir
Al
abrir
Durante la
recuperacin
Al finalizar la
recuperacin
Despus de cerrar
TRUE
ORA-1001
FALSE
TRUE
ORA-1001
FALSE
ORA-1001 0
**
ORA-1001
OPEN nombre_cursor;
LOOP
FETCH nombre_cursor INTO lista_variables;
EXIT WHEN nombre_cursor%NOTFOUND;
/* Procesamiento de los registros recuperados */
END LOOP;
CLOSE nombre_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;
LOOP
FETCH cpaises INTO co_pais,descripcion,continente;
EXIT WHEN cpaises%NOTFOUND;
dbms_output.put_line(descripcion);
END LOOP;
CLOSE cpaises;
END;
Otra forma es por medio de un bucle WHILE LOOP. La instruccin FECTH aparece
dos veces.
OPEN nombre_cursor;
FETCH nombre_cursor INTO lista_variables;
WHILE nombre_cursor%FOUND
LOOP
/* Procesamiento de los registros recuperados */
FETCH nombre_cursor INTO lista_variables;
END LOOP;
CLOSE nombre_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;
WHILE cpaises%found
LOOP
dbms_output.put_line(descripcion);
FETCH cpaises INTO co_pais,descripcion,continente;
END LOOP;
CLOSE cpaises;
END;
Por ltimo podemos usar un bucle FOR LOOP. Es la forma ms corta ya que el
cursor es implicitamente se ejecutan las instrucciones OPEN, FECTH y CLOSE.
BEGIN
FOR REG IN (SELECT * FROM PAISES)
LOOP
dbms_output.put_line(reg.descripcion);
END LOOP;
END;
Excepciones en PL/SQL
Manejo de excepciones
En PL/SQL una advertencia o condicin de error es llamada una excepcin.
Las excepciones se controlan dentro de su propio bloque.La estructura de bloque
de una excepcin se muestra a continuacin.
DECLARE
-- Declaraciones
BEGIN
-- Ejecucion
EXCEPTION
-- Excepcion
END;
Cuando ocurre un error, se ejecuta la porcin del programa marcada por el
bloque EXCEPTION, transfirindose 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 excepcion ser
capturada en el bloque WHEN OTHERS THEN.
DECLARE
-- Declaraciones
BEGIN
-- Ejecucion
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- Se ejecuta cuando ocurre una excepcion de tipo NO_DATA_FOUND
WHEN ZERO_DIVIDE THEN
-- Se ejecuta cuando ocurre una excepcion de tipo ZERO_DIVIDE
WHEN OTHERS THEN
-- Se ejecuta cuando ocurre una excepcion de un tipo no tratado
-- en los bloques anteriores
END;
Como ya hemos dicho cuando ocurre un error, se ejecuta el bloque EXCEPTION,
transfirindose el control a las sentencias del bloque. Una vez finalizada la ejecucin
del bloque de EXCEPTION no se continua ejecutando el bloque anterior.
Si existe un bloque de excepcion apropiado para el tipo de excepcin se ejecuta
dicho bloque. Si no existe un bloque de control de excepciones adecuado al tipo de
excepcion se ejecutar el bloque de excepcion WHEN OTHERS THEN (si
existe!). WHEN OTHERSdebe ser el ltimo manejador de excepciones.
Las excepciones pueden ser definidas en forma interna o explcitamente por el
usuario. Ejemplos de excepciones definidas en forma interna son la divisin por cero
y la falta de memoria en tiempo de ejecucin. 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 explcitamente
utilizando la sentencia RAISE.
Con las excepciones se pueden manejar los errores cmodamente sin necesidad
de mantener mltiples chequeos por cada sentencia escrita. Tambin provee
claridad en el cdigo ya que permite mantener las rutinas correspondientes al
tratamiento de los errores de forma separada de la lgica del negocio.
Excepciones predefinidas
PL/SQL proporciona un gran nmero de excepciones predefinidas que permiten
controlar las condiciones de error ms habituales.
Las excepciones predefinidas no necesitan ser declaradas. Simplemente se
utilizan cuando estas son lanzadas por algn error determinado.
La siguiente es la lista de las excepciones predeterminadas por PL/SQL y una
breve descripcin de cundo son accionadas:
Excepcion
ACCESS_INTO_NULL
COLLECTION_IS_NULL
Se ejecuta ...
El programa intent asignar valores a los atributos
de un objeto no inicializado
El programa intent asignar valores a una tabla
SQLCODE
-6530
-6531
anidada an no inicializada
El programa intent abrir un cursor que ya se
encontraba abierto. Recuerde que un cursor de
CURSOR_ALREADY_OPEN
-6511
ciclo FOR automticamente lo abre y ello no se
debe especificar con la sentencia OPEN
El programa intent almacenar valores duplicados
DUP_VAL_ON_INDEX
en una columna que se mantiene con restriccin de -1
integridad de un ndice nico (unique index)
El programa intent efectuar una operacin no
INVALID_CURSOR
-1001
vlida sobre un cursor
En una sentencia SQL, la conversin de una cadena
INVALID_NUMBER
de caracteres hacia un nmero falla cuando esa
-1722
cadena no representa un nmero vlido
El programa intent conectarse a Oracle con un
LOGIN_DENIED
-1017
nombre de usuario o password invlido
Una sentencia SELECT INTO no devolvi valores o el
NO_DATA_FOUND
programa referenci un elemento no inicializado en 100
una tabla indexada
El programa efectu una llamada a Oracle sin estar
NOT_LOGGED_ON
-1012
conectado
PROGRAM_ERROR
PL/SQL tiene un problema interno
-6501
Los elementos de una asignacin (el valor a asignar
y la variable que lo contendr) tienen tipos
ROWTYPE_MISMATCH
incompatibles. Tambin se presenta este error
-6504
cuando un parmetro pasado a un subprograma no
es del tipo esperado
El parmetro SELF (el primero que es pasado a un
SELF_IS_NULL
-30625
mtodo MEMBER) es nulo
STORAGE_ERROR
La memoria se termin o est corrupta
-6500
El programa est tratando de referenciar un
SUBSCRIPT_BEYOND_COUN elemento de un arreglo indexado que se encuentra
-6533
T
en una posicin ms grande que el nmero real de
elementos de la coleccin
El programa est referenciando un elemento de un
SUBSCRIPT_OUTSIDE_LIMI
arreglo utilizando un nmero fuera del rango
-6532
T
permitido (por ejemplo, el elemento -1)
La conversin de una cadena de caracteres hacia
SYS_INVALID_ROWID
un tipo rowid fall porque la cadena no representa -1410
un nmero
Se excedi el tiempo mximo de espera por un
TIMEOUT_ON_RESOURCE
-51
recurso en Oracle
Una sentencia SELECT INTO devuelve ms de una
TOO_MANY_ROWS
-1422
fila
Ocurri un error aritmtico, de conversin o
truncamiento. Por ejemplo, sucede cuando se
VALUE_ERROR
-6502
intenta calzar un valor muy grande dentro de una
variable ms pequea
ZERO_DIVIDE
El programa intent efectuar una divisin por cero -1476
RAISE_APPLICATION_ERROR(<error_num>,<mensaje>);
Siendo:
DECLARE
v_div NUMBER;
BEGIN
SELECT 1/0 INTO v_div FROM DUAL;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20001,'No se puede dividir por cero');
END;
Procedimientos almacenados
Un procedimiento es un subprograma que ejecuta una accin especifica y que no
devuelve ningn valor. Un procedimiento tiene un nombre, un conjunto de
parmetros (opcional) y un bloque de cdigo.
La sintaxis de un procedimiento almacenado es la siguiente:
CREATE OR REPLACE
PROCEDURE Actualiza_Saldo(cuenta NUMBER,
new_saldo NUMBER)
IS
-- Declaracion de variables locales
BEGIN
-- Sentencias
UPDATE SALDOS_CUENTAS
SET SALDO = new_saldo,
FX_ACTUALIZACION = SYSDATE
WHERE CO_CUENTA = cuenta;
END Actualiza_Saldo;
Tambin podemos asignar un valor por defecto a los parmetros, utilizando la
clausula DEFAULT o el operador de asigancin (:=) .
CREATE OR REPLACE
PROCEDURE Actualiza_Saldo(cuenta NUMBER,
new_saldo NUMBER DEFAULT 10 )
IS
-- Declaracion de variables locales
BEGIN
-- Sentencias
UPDATE SALDOS_CUENTAS
SET SALDO = new_saldo,
FX_ACTUALIZACION = SYSDATE
WHERE CO_CUENTA = cuenta;
END Actualiza_Saldo;
Una vez creado y compilado el procedimiento almacenado podemos ejecutarlo. Si
el sistema nos indica que el procedimiento se ha creado con errores de compilacin
podemos ver estos errores de compilacion con la orden SHOW ERRORS en SQL
*Plus.
Existen dos formas de pasar argumentos a un procedimiento almacenado a la
hora de ejecutarlo (en realidad es vlido para cualquier subprograma). Estas son:
BEGIN
Actualiza_Saldo(200501,2500);
COMMIT;
END;
BEGIN
Actualiza_Saldo(cuenta => 200501,new_saldo
COMMIT;
=> 2500);
END;
Funciones en PL/SQL
Una funcin es un subprograma que devuelve un valor.
La sintaxis para construir funciones es la siguiente:
CREATE OR REPLACE
DECLARE
Valor NUMBER;
BEGIN
Valor := fn_Obtener_Precio('000100');
END;
Las funciones pueden utilizarse en sentencias SQL de manipulacin de datos
(SELECT, UPDATE, INSERT y DELETE):
SELECT CO_PRODUCTO,
DESCRIPCION,
fn_Obtener_Precio(CO_PRODUCTO)
FROM PRODUCTOS;
Triggers
Declaracin de triggers
Un trigger es un bloque PL/SQL asociado a una tabla, que se ejecuta como
consecuencia de una determinada instruccin SQL (una operacin DML: INSERT,
UPDATE o DELETE) sobre dicha tabla.
La sintaxis para crear un trigger es la siguiente:
sobre una fila de la tabla. Si se acompaa del modificador WHEN, se establece una
restriccin; el trigger solo actuar, sobre las filas que satisfagan la restriccin.
La siguiente tabla resume los contenidos anteriores.
Valor
INSERT, DELETE, UPDATE
BEFORE , AFTER
FOR EACH ROW
Descripcin
Define qu tipo de orden DML provoca la activacin del disparador.
Define si el disparador se activa antes o despus de que se ejecute la orden.
Los disparadores con nivel de fila se activan una vez por cada fila afectada por la
orden que provoc el disparo. Los disparadores con nivel de orden se activan slo
una vez, antes o despus de la orden. Los disparadores con nivel de fila se
identifican por la clusula FOR EACH ROW en la definicin del disparador.
La clusula WHEN slo es vlida para los disparadores con nivel de fila.
Dentro del ambito de un trigger disponemos de las variables OLD y NEW . Estas
variables se utilizan del mismo modo que cualquier otra variable PL/SQL, con la
salvedad de que no es necesario declararlas, son de tipo %ROWTYPE y contienen
una copia del registro antes (OLD) y despues(NEW) de la accin SQL (INSERT,
UPDATE, DELTE) que ha ejecutado el trigger. Utilizando esta variable podemos
acceder a los datos que se estn insertando, actualizando o borrando.
El siguiente ejemplo muestra un trigger que inserta un registro en la tabla
PRECIOS_PRODUCTOS cada vez que insertamos un nuevo registro en la tabla
PRODUTOS:
ACCION
SQL
INSERT
UPDATE
DELETE
OLD
No definido; todos los campos toman valor
NULL.
Valores originales de la fila, antes de la
actualizacin.
Valores, antes del borrado de la fila.
NEW
Valores que sern insertados cuando se complete la
orden.
Nuevos valores que sern escritos cuando se complete la
orden.
No definidos; todos los campos toman el valor NULL.
Los registros OLD y NEW son slo vlidos dentro de los disparadores con nivel de
fila.
Podemos usar OLD y NEW como cualquier otra variable PL/SQL.
Predicado
INSERTING
UPDATING
DELETING
Comportamiento
TRUE si la orden de disparo es INSERT; FALSE en otro caso.
TRUE si la orden de disparo es UPDATE; FALSE en otro caso.
TRUE si la orden de disparo es DELETE; FALSE en otro caso.
DECLARE
idx NUMBER;
FUNCTION fn_multiplica_x2(num NUMBER)
RETURN NUMBER
IS
result NUMBER;
BEGIN
result := num *2;
BEGIN
return result;
END fn_multiplica_x2;
FOR idx IN 1..10
LOOP
dbms_output.put_line
('Llamada a la funcion ... '||TO_CHAR(fn_multiplica_x2(idx)));
END LOOP;
END;
Notese que se utiliza la funcion TO_CHAR para convertir el resultado de la funcin
fn_multiplica_x2 (numrico) en alfanumrico y poder mostrar el resultado por
pantalla.
Packages en PL/SQL
Un paquete es una estructura que agrupa objetos de PL/SQL
compilados(procedures, funciones, variables, tipos ...) en la base de datos. Esto nos
permite agrupar la funcionalidad de los procesos en programas.
Lo primero que debemos tener en cuenta es que los paquetes estn formados
por dos partes: la especificacin y el cuerpo. La especificacin del un paquete y
su cuerpo se crean por separado.
La especificacin es la interfaz con las aplicaciones. En ella es posible declarar los
tipos, variables, constantes, excepciones, cursores y subprogramas disponibles para
su uso posterior desde fuera del paquete. En la especificacin del paquete slo se
declaran los objetos (procedures, funciones, variables ...), no se implementa el
cdigo. Los objetos declarados en la especificacin del paquete son accesibles
desde fuera del paquete por otro script de PL/SQL o programa. Haciendo una
analoga con el mundo de C, la especificacin es como el archivo de cabecera de un
programa en C.
BODY
<pkgName>
[EXCEPTION]
-- Control de excepciones
END;
END <pkgName>;
El siguiente ejemplo crea un paquete llamado PKG_CONTABILIDAD.
Para crear la especificacin del paquete:
fila cDatos%ROWTYPE;
BEGIN
OPEN cDatos(mes);
LOOP FETCH cDatos INTO fila;
EXIT WHEN cDatos%NOTFOUND;
/* Procesamiento de los registros recuperados */
END LOOP;
CLOSE cDatos;
EXCEPTION
WHEN OTHERS THEN
RAISE ERROR_CONTABILIZAR;
END Contabilizar;
END PKG_CONTABILIDAD;
Es posible modificar el cuerpo de un paquete sin necesidad de alterar por ello la
especificacin del mismo.
Los paquetes pueden llegar a ser programas muy complejos y suelen almacenar
gran parte de la lgica de negocio.
Registros PL/SQL
Cuando vimos los tipos de datos, omitimos intencionadamente ciertos tipos de
datos.
Estos son:
Registros
Tablas de PL
VARRAY
Declaracin de un registro.
Un registnslpwdro es una estructura de datos en PL/SQL, almacenados en
campos, cada uno de los cuales tiene su propio nombre y tipo y que se tratan como
una sola unidad lgica.
Los campos de un registro pueden ser inicializados y pueden ser definidos como
NOT NULL. Aquellos campos que no sean inicializados explcitamente, se
inicializarn a NULL.
La sintaxis general es la siguiente:
DECLARE
TYPE PAIS IS RECORD
(
CO_PAIS
NUMBER ,
DESCRIPCION VARCHAR2(50),
CONTINENTE VARCHAR2(20)
);
/* Declara una variable identificada por miPAIS de tipo PAIS
Esto significa que la variable miPAIS tendr los campos
ID, DESCRIPCION y CONTINENTE.
*/
miPAIS PAIS;
BEGIN
/* Asignamos valores a los campos de la variable.
*/
miPAIS.CO_PAIS := 27;
miPAIS.DESCRIPCION := 'ITALIA';
miPAIS.CONTINENTE := 'EUROPA';
END;
Los registros pueden estar anidados. Es decir, un campo de un registro puede ser
de un tipo de dato de otro registro.
DECLARE
TYPE PAIS IS RECORD
(CO_PAIS
NUMBER ,
DESCRIPCION VARCHAR2(50),
CONTINENTE VARCHAR2(20)
);
TYPE MONEDA IS RECORD
( DESCRIPCION VARCHAR2(50),
PAIS_MONEDA PAIS );
miPAIS
PAIS;
miMONEDA MONEDA;
BEGIN
/* Sentencias
*/
END;
Pueden asignarse todos los campos de un registro utilizando una sentencia
SELECT. En este caso hay que tener cuidado en especificar las columnas en el orden
conveniente segn la declaracin de los campos del registro. Para este tipo de
asignacin es muy frecuente el uso del atributo %ROWTYPE que veremos ms
adelante.
DECLARE
TYPE PAIS IS RECORD ...
miPAIS PAIS;
otroPAIS PAIS;
BEGIN
miPAIS.CO_PAIS := 27;
miPAIS.DESCRIPCION := 'ITALIA';
miPAIS.CONTINENTE := 'EUROPA';
otroPAIS := miPAIS;
END;
DECLARE
miPAIS PAISES%ROWTYPE;
BEGIN
/* Sentencias ... */
END;
Lo cual significa que el registro miPAIS tendr la siguiente estructura: CO_PAIS
NUMBER, DESCRIPCION VARCHAR2(50), CONTINENTE VARCHAR2(20).
De esta forma se crea el registro de forma dinamic y se podrn asignar valores a
los campos de un registro a travs de un select sobre la tabla, vista o cursor a partir
de la cual se creo el registro.
Tablas PL/SQL
Declaracin de tablas de PL/SQL
Las tablas de PL/SQL son tipos de datos que nos permiten almacenar varios
valores del mismo tipo de datos.
Una tabla PL/SQL :
Es similar a un array
Tiene dos componenetes: Un ndice de tipo BINARY_INTEGER que permite
acceder a los elementos en la tabla PL/SQL y una columna de escalares o registros
que contiene los valores de la tabla PL/SQL
Puede incrementar su tamao dinmicamente.
La sintaxis general para declarar una tabla de PL es la siguiente:
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;
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;
DECLARE
TYPE ARR_CIUDADES IS TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER;
misCiudades ARR_CIUDADES;
BEGIN
misCiudades(1) := 'MADRID';
misCiudades(2) := 'BILBAO';
misCiudades(3) := 'MALAGA';
END;
FOR i IN misCiudades.FIRST..misCiudades.LAST
LOOP
dbms_output.put_line(misCiudades(i));
END LOOP;
DECLARE
TYPE ARR_CIUDADES IS TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER;
misCiudades ARR_CIUDADES;
BEGIN
misCiudades(1) := 'MADRID';
misCiudades(3) := 'MALAGA';
FOR i IN misCiudades.FIRST..misCiudades.LAST
LOOP
IF misCiudades.EXISTS(i) THEN
dbms_output.put_line(misCiudades(i));
ELSE
dbms_output.put_line('El elemento no existe:'||TO_CHAR(i));
END IF;
END LOOP;
END;
DECLARE
TYPE ARR_CIUDADES IS TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER;
misCiudades ARR_CIUDADES;
BEGIN
misCiudades(1) := 'MADRID';
misCiudades(3) := 'MALAGA';
/* Devuelve 2, ya que solo hay dos elementos con valor */
dbms_output.put_line(
'El nmero de elementos es:'||misCiudades.COUNT);
END;
DECLARE
TYPE ARR_CIUDADES IS TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER;
misCiudades ARR_CIUDADES;
BEGIN
misCiudades(1) := 'MADRID';
misCiudades(3) := 'MALAGA';
/* Devuelve 1, ya que el elemento 2 no existe */
dbms_output.put_line(
'El elemento previo a 3 es:' || misCiudades.PRIOR(3));
END;
DECLARE
TYPE ARR_CIUDADES IS TABLE OF VARCHAR2(50) INDEX BY BINARY_INTEGER;
misCiudades ARR_CIUDADES;
BEGIN
misCiudades(1) := 'MADRID';
misCiudades(3) := 'MALAGA';
/* Devuelve 3, ya que el elemento 2 no existe */
dbms_output.put_line(
'El elemento siguiente es:' || misCiudades.NEXT(1));
END;
VARRAYS
Definicin de VARRAYS.
Un varray se manipula de forma muy similar a las tablas de PL, pero se
implementa de forma diferente. Los elementos en el varray se almacenan
comenzando en el ndice 1 hasta la longitud mxima declarada en el tipo varray.
La sintaxis general es la siguiente:
BOOLEAN
NCHAR
NCLOB
NVARCHAR(n)
REF CURSOR
TABLE
VARRAY
Sin embargo se puede especificar el tipo utilizando los
atributos %TYPE y %ROWTYPE.
Los VARRAY deben estar inicializados antes de poder utilizarse. Para inicializar
un VARRAY se utiliza un constructor (podemos inicializar el VARRAY en la seccin
DECLARE o bien dentro del cuerpo del bloque):
DECLARE
/* Declaramos el tipo VARRAY de cinco elementos VARCHAR2*/
TYPE t_cadena IS VARRAY(5) OF VARCHAR2(50);
/* Asignamos los valores con un constructor */
v_lista t_cadena:= t_cadena('Aitor', 'Alicia', 'Pedro','','');
BEGIN
v_lista(4) := 'Tita';
v_lista(5) := 'Ainhoa';
END;
El tamao de un VARRAY se establece mediante el nmero
de parmetros utilizados en el constructor, si declaramos un VARRAY de cinco
elementos pero al inicializarlo pasamos slo tres parmetros al constructor, el
tamao del VARRAY ser tres. Si se hacen asignaciones a elementos que queden
fuera del rango se producir un error.
El tamao de un VARRAY podr aumentarse utilizando la funcin EXTEND, pero
nunca con mayor dimensin que la definida en la declaracin del tipo. Por ejemplo,
la variable v_lista que slo tiene 3 valores definidos por lo que se podra ampliar
hasta cinco elementos pero no ms all.
Un VARRAY comparte con las tablas de PL todas las funciones vlidas para ellas,
pero aade las siguientes:
Para poder crear tablas con campos de tipo VARRAY debemos crear el VARRAY
como un objeto de la base de datos.
La sintaxis general es:
BULK COLLECT
PL/SQL nos permite leer varios registros en una tabla de PL con un nico acceso
a travs de la instruccin BULK COLLECT.
Esto nos permitir reducir el nmero de accesos a disco, por lo que
optimizaremos el rendimiento de nuestras aplicaciones. Como contrapartida el
consumo de memoria ser mayor.
DECLARE
TYPE t_descripcion IS TABLE OF PAISES.DESCRIPCION%TYPE;
TYPE t_continente IS TABLE OF PAISES.CONTINENTE%TYPE;
v_descripcion t_descripcion;
v_continente t_continente;
BEGIN
SELECT DESCRIPCION,
CONTINENTE
BULK COLLECT INTO v_descripcion, v_continente
FROM PAISES;
FOR i IN v_descripcion.FIRST .. v_descripcion.LAST LOOP
dbms_output.put_line(v_descripcion(i) || ', ' || v_continente(i));
END LOOP;
END;
/
Podemos utilizar BULK COLLECT con registros de PL.
DECLARE
TYPE PAIS IS RECORD (CO_PAIS
NUMBER ,
DESCRIPCION VARCHAR2(50),
CONTINENTE VARCHAR2(20));
TYPE t_paises IS TABLE OF PAIS;
v_paises t_paises;
BEGIN
SELECT CO_PAIS, DESCRIPCION, CONTINENTE
BULK COLLECT INTO v_paises
FROM PAISES;
FOR i IN v_paises.FIRST .. v_paises.LAST LOOP
dbms_output.put_line(v_paises(i).DESCRIPCION ||
', ' || v_paises(i).CONTINENTE);
END LOOP;
END;
/
Tambien podemos utilizar el atributo ROWTYPE.
DECLARE
TYPE t_paises IS TABLE OF PAISES%ROWTYPE;
v_paises t_paises;
BEGIN
SELECT CO_PAIS, DESCRIPCION, CONTINENTE
BULK COLLECT INTO v_paises
FROM PAISES;
FOR i IN v_paises.FIRST .. v_paises.LAST LOOP
dbms_output.put_line(v_paises(i).DESCRIPCION ||
', ' || v_paises(i).CONTINENTE);
END LOOP;
END;
/
Transacciones
Una transaccin es un conjunto de operaciones que se ejecutan en una base de
datos, y que son tratadas como una nica unidad lgica por el SGBD.
Es decir, una transaccin es una o varias sentencias SQL que se ejecutan en una
base de datos como una nica operacin, confirmandose o deshaciendose en grupo.
No todas las operaciones SQL son transaccionales. Slo son transaccionales las
operaciones correspondiente al DML, es decir,
sentencias SELECT, INSERT, UPDATE y DELETE
Para confirmar una transaccin se utiliza la sentencia COMMIT. Cuando
realizamos COMMIT los cambios se escriben en la base de datos.
Para deshacer una transaccin se utiliza la sentencia ROLLBACK. Cuando
realizamos ROLLBACK se deshacen todas las modificaciones realizadas por la
DECLARE
importe NUMBER;
ctaOrigen VARCHAR2(23);
ctaDestino VARCHAR2(23);
BEGIN
importe := 100;
ctaOrigen := '2530 10 2000 1234567890';
ctaDestino := '2532 10 2010 0987654321';
UPDATE CUENTAS SET SALDO = SALDO - importe
WHERE CUENTA = ctaOrigen;
UPDATE CUENTAS SET SALDO = SALDO + importe
WHERE CUENTA = ctaDestino;
INSERT INTO MOVIMIENTOS
(CUENTA_ORIGEN, CUENTA_DESTINO,IMPORTE, FECHA_MOVIMIENTO)
VALUES
(ctaOrigen, ctaDestino, importe*(-1), SYSDATE);
INSERT INTO MOVIMIENTOS
(CUENTA_ORIGEN, CUENTA_DESTINO,IMPORTE, FECHA_MOVIMIENTO)
VALUES
(ctaDestino,ctaOrigen, importe, SYSDATE);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Error en la transaccion:'||SQLERRM);
dbms_output.put_line('Se deshacen las modificaciones);
ROLLBACK;
END;
Transacciones autnomas
En ocasiones es necesario que los datos escritos por parte de una transaccin
sean persistentes a pesar de que la transaccion se deshaga con ROLLBACK.
PL/SQL permite marcar un bloque con PRAGMA
AUTONOMOUS_TRANSACTION. Con esta directiva marcamos el subprograma
para que se comporte como transaccin diferente a la del proceso principal,
llevando el control de COMMIT o ROLLBACKindependiente.
Observese el siguiente ejemplo. Primero creamos un procedimiento y lo
marcamos con PRAGMA AUTONOMOUS_TRANSACTION.
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO LOG_APLICACION
(CO_ERROR, DESCRIPICION, FX_ERROR)
VALUES
(SQ_ERROR.NEXTVAL, descripcion, SYSDATE);
COMMIT; -- Este commit solo afecta a la transaccion autonoma
END ;
A continuacin utilizamos el procedimiento desde un bloque de PL/SQL:
DECLARE
producto PRECIOS%TYPE;
BEGIN
producto := '100599';
INSERT INTO PRECIOS
(CO_PRODUCTO, PRECIO, FX_ALTA)
VALUES
(producto, 150, SYSDATE);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
Grabar_Log(SQLERRM);
ROLLBACK;
/* Los datos grabados por "Grabar_Log" se escriben en la base
de datos a pesar del ROLLBACK, ya que el procedimiento est
marcado como transaccin autonoma.
*/
END;
Es muy comn que, por ejemplo, en caso de que se produzca algn tipo de error
queramos insertar un registro en una tabla de log con el error que se ha
produccido y hacer ROLLBACK de la transaccin. Pero si hacemos ROLLBACK de la
transaccin tambien lo hacemos de la insertcin del log.
SQL Dinamico
Sentencias DML con SQL dinamico
PL/SQL ofrece la posibilidad de ejecutar sentencias SQL a partir de cadenas de
caracteres. Para ello debemos emplear la instruccinEXECUTE IMMEDIATE.
Podemos obtener informacin acerca de nmero de filas afectadas por la
instruccin ejecutada por EXEXUTE IMMEDIATEutilizando SQL%ROWCOUNT.
El siguiente ejemplo muestra la ejecucin de un comando SQL dinamico.
DECLARE
ret NUMBER;
FUNCTION fn_execute RETURN NUMBER IS
sql_str VARCHAR2(1000);
BEGIN
sql_str := 'UPDATE DATOS SET NOMBRE = ''NUEVO NOMBRE''
WHERE CODIGO = 1';
EXECUTE IMMEDIATE sql_str;
RETURN SQL%ROWCOUNT;
END fn_execute ;
BEGIN
ret := fn_execute();
dbms_output.put_line(TO_CHAR(ret));
END;
Podemos adems parametrizar nuestras consultas a travs de variables host.
Una variable host es una variable que pertenece al programa que est ejecutando
la sentencia SQL dinmica y que podemos asignar en el interior de la sentencia SQL
con la palabra claveUSING . Las variables host van precedidas de dos puntos ":".
El siguiente ejemplo muestra el uso de variables host para parametrizar una
sentencia SQL dinamica.
DECLARE
ret NUMBER;
FUNCTION fn_execute (nombre VARCHAR2, codigo NUMBER) RETURN NUMBER
IS
sql_str VARCHAR2(1000);
BEGIN
sql_str := 'UPDATE DATOS SET NOMBRE = :new_nombre
WHERE CODIGO = :codigo';
EXECUTE IMMEDIATE sql_str USING nombre, codigo;
RETURN SQL%ROWCOUNT;
END fn_execute ;
BEGIN
ret := fn_execute('Devjoker',1);
dbms_output.put_line(TO_CHAR(ret));
END;
DECLARE
str_sql VARCHAR2(255);
l_cnt
VARCHAR2(20);
BEGIN
END;
Trabajar con cursores explicitos es tambin muy fcil. nicamente destacar el uso
de REF CURSOR para declarar una variable para referirnos al cursor generado con
SQL dinamico.
DECLARE
TYPE CUR_TYP IS REF CURSOR;
c_cursor
CUR_TYP;
fila PAISES%ROWTYPE;
v_query
VARCHAR2(255);
BEGIN
v_query := 'SELECT * FROM PAISES';
OPEN c_cursor FOR v_query;
LOOP
FETCH c_cursor INTO fila;
EXIT WHEN c_cursor%NOTFOUND;
dbms_output.put_line(fila.DESCRIPCION);
END LOOP;
CLOSE c_cursor;
END;
Las varibles host tambien se pueden utilizar en los cursores.
DECLARE
TYPE cur_typ IS REF CURSOR;
c_cursor
CUR_TYP;
fila PAISES%ROWTYPE;
v_query
VARCHAR2(255);
codigo_pais VARCHAR2(3) := 'ESP';
BEGIN
v_query := 'SELECT * FROM PAISES WHERE CO_PAIS = :cpais';
OPEN c_cursor FOR v_query USING codigo_pais;
LOOP
FETCH c_cursor INTO fila;
EXIT WHEN c_cursor%NOTFOUND;
dbms_output.put_line(fila.DESCRIPCION);
END LOOP;
CLOSE c_cursor;
END;
SYSDATE
Devuelve la fecha del sistema:
NVL
NVL(<expresion>, <valor>)
El siguiente ejemplo devuelve 0 si el precio es nulo, y el precio cuando est
informado:
DECODE
Decode proporciona la funcionalidad de una sentencia de control de flujo ifelseif-else.
TO_DATE
Convierte una expresin al tipo fecha. El parmetro opcional formato indica el
formato de entrada de la expresin no el de salida.
TO_DATE(<expresion>, [<formato>])
En este ejemplo convertimos la expresion '01/12/2006' de tipo CHAR a una fecha
(tipo DATE). Con el parmetro formato le indicamos que la fecha est escrita como
da-mes-ao para que devuelve el uno de diciembre y no el doce de enero.
SELECT TO_DATE('01/12/2006',
'DD/MM/YYYY')
FROM DUAL;
TO_CHAR
Convierte una expresin al tipo CHAR. El parmetro opcional formato indica el
formato de salida de la expresin.
TO_CHAR(<expresion>, [<formato>])
SELECT TO_CHAR(SYSDATE, 'DD/MM/YYYYY')
FROM DUAL;
TO_NUMBER
Convierte una expresion alfanumrica en numerica. Opcionalmente podemos
especificar el formato de salida.
TO_NUMBER(<expresion>, [<formato>])
TRUNC
Trunca una fecha o nmero.
Si el parmetro recibido es una fecha elimina las horas, minutos y segundos de
la misma.
LENGTH
Devuelve la longitud de un tipo CHAR.
INSTR
Busca una cadena de caracteres dentro de otra. Devuelve la posicion de la
ocurrencia de la cadena buscada.
Su sintaxis es la siguiente:
REPLACE
Reemplaza un texto por otro en un expresion de busqueda.
SUBSTR
Obtiene una parte de una expresion, desde una posicin de inicio hasta una
determinada longitud.
UPPER
Convierte una expresion alfanumerica a maysculas.
LOWER
ROWIDTOCHAR
Convierte un ROWID a tipo caracter.
SELECT ROWIDTOCHAR(ROWID)
FROM DUAL;
RPAD
Aade N veces una determinada cadena de caracteres a la derecha una
expresin. Muy util para generar ficheros de texto de ancho fijo.
LPAD
Aade N veces una determinada cadena de caracteres a la izquierda de una
expresin. Muy util para generar ficheros de texto de ancho fijo.
RTRIM
Elimina los espacios en blanco a la derecha de una expresion
')
LTRIM
Elimina los espacios en blanco a la izquierda de una expresion
Hola Mundo')
TRIM
Elimina los espacios en blanco a la izquierda y derecha de una expresion
Hola Mundo
')
MOD
Devuelve el resto de la divisin entera entre dos nmeros.
MOD(<dividendo>, <divisor> )
SELECT MOD(20,15) -- Devuelve el modulo de dividir 20/15
FROM DUAL
Secuencias
ORACLE proporciona los objetos de secuencia para la generacin de cdigos
numericos automticos.
Las secuencias son una solucin fcil y elegante al problema de los codigos
autogenerados.
LA sintaxis general es la siguiente:
SELECT SQ_PRODUCTOS.NEXTVAL
FROM DUAL;
Podemos obtener el ltimo valor generado por la secuencia con la
funcin CURRVAL. Para poder ejecutar la funcin CURRVAL debemos haber
ejecutado previamente la funcin NEXTVAL.
SELECT SQ_PRODUCTOS.CURRVAL
FROM DUAL;
Para eliminar una secuencia definitivamente de la base de datos debemos utilizar
la sentencia DROP.
PL/SQL y Java
Otra de la virtudes de PL/SQL es que permite trabajar conjuntamente con Java.
PL/SQL es un excelente lenguaje para la gestion de informacin pero en
ocasiones, podemos necesitar de un lenguaje de programacin ms potente. Por
ejemplo podramos necesitar consumir un servicio Web, conectar a otro servidor,
trabajar con Sockets .... Para estos casos podemos trabajar conjuntamente con
PL/SQL y Java.
Para poder trabajar con Java y PL/SQL debemos realizar los siguientes pasos:
mtodos de la clase java que queramos invocar desde PL/SQL deben ser
estaticos.
loadJava -help
loadJava -u usario/password -v -f -r OracleJavaClass.class
loadJava -u usario/password -v -f -r OracleJavaClass.java
Una vez que tenemos listo el programa de Java debemos integrarlo con PL/SQL.
Esto se realiza a travs de subprogramas de recubrimiento llamados Wrappers.
No podemos crear un Wrapper en un bloque anonimo.
La sintaxis general es la siguiente:
CREATE OR REPLACE
FUNCTION Saluda_wrap (nombre VARCHAR2)
RETURN VARCHAR2
AS
LANGUAGE JAVA NAME
'OracleJavaClass.Saluda(java.lang.String) return java.lang.String';
Una vez creado el wrapper, podremos ejecutarlo como cualquier otra funcion o
procedure de PL/SQL. Debemos crear un wrapper por cada funcin java que
queramos ejecutar desde PL/SQL.
Cuando ejecutemos el wrapper, es decir, la funcin "Saluda_wrap", internamente
se ejecutar la clase java y se invocar el mtodo esttico
"OracleJavaClass.Saluda".
Un aspecto a tener en cuenta es que es necesario proporcionar el
nombre del tipo java completo, es decir, debemos especificar
java.lang.String en lugar de nicamente String.
SELECT SALUDA_WRAP('DEVJOKER')
FROM DUAL;
La ejecucin de este ejemplo en SQL*Plus genera la siguiente salida:
Cc