Está en la página 1de 19

Firebird: SQL Procedimental (PSQL) Tema 6

FIREBIRD:
SQL
PROCEDIMENTAL
(PSQL)

Vicente Tejero Trueba Pag 1 IES Pedro Espinosa


Firebird: SQL Procedimental (PSQL) Tema 6

FIREBIRD: SQL PROCEDIMENTAL (PSQL)

1.- INTRODUCCION.............................................................................................................................. 1
2.- EXTENSIONES DEL LENGUAJE................................................................................................... 2
2.1.- Terminadores. .............................................................................................................................. 2
2.2.- Variables...................................................................................................................................... 3
2.3.- Sentencias básicas........................................................................................................................ 4
2.4.- Cursores. ...................................................................................................................................... 5
2.5.- Sentencias control de flujo. ......................................................................................................... 8
2.6.- Ejecución de código..................................................................................................................... 9
2.7.- Eventos. ....................................................................................................................................... 9
3.- PROCEDIMIENTOS ALMACENADOS........................................................................................ 11
4.- TRIGGERS....................................................................................................................................... 13
5.- EXCEPCIONES ............................................................................................................................... 16

Vicente Tejero Trueba Pag i IES Pedro Espinosa


Firebird: SQL Procedimental (PSQL) Tema 6

1.- INTRODUCCION
Uno de los grandes beneficios de la implementación de SQL en Firebird es la posibilidad de
compilar y ejecutar modulos de código interno (procedimientos almacenados y triggers) desarrollados
por un programador. Para ello se tiene PSQL, una sintaxis que combina sentencias de DML con una
extensión para programación.

Se habla en este caso de programación en el lado del servidor, ya que el código se ejecutará en
la máquina del servidor por el propio servidor. Esto tiene sus ventajas:
- Diseño modular: Todas las aplicaciones que acceden a la misma base de datos comparten
los procedimientos almacenados y por tanto se centralizan las reglas de empresa, se
reutiliza código y se reduce el tamaño de las aplicaciones.
- Facilidad en mantenimiento: Cuando se realiza un cambio en un procedimiento, éste se
traslada de forma automática a todas las aplicaciones que lo usan.
- Mejora de eficiencia: El procesado complejo se realiza en el servidor reduciendo el trafico
de red y la carga en los clientes.
- Economía en la arquitectura: Las aplicaciones clientes se pueden orientar hacia la captura
de datos y tareas interactivas mientas el trabajo complejo con datos se realiza en el
servidor.
- Funcionalidad extra: Operaciones que no se pueden realizar fácilmente mediante sentencias
SQL pueden implementarse mediante procedimientos almacenados.

Cada módulo creado podrá ser:

- Procedimiento almacenado: Se pueden utilizar como procedimientos seleccionables


(sustituyen a una tabla o vista en una sentencia SELECT) o como procedimientos
ejecutables (ejecutar una operación o conjunto de operaciones en el lado del servidor) que
se pueden llamar mediante EXECUTE PROCEDURE o desde otro procedimiento o trigger.

- Trigger: Es una rutina asociada a una tabla o vista que se lanza de forma automática
cuando se realiza una operación de inserción, borrado o actualización de una fila. Un
trigger nunca se llama directamente.

Se suelen usar los triggers para realizar actualizaciones de datos ante cambios en filas,
validar datos de entrada, transformaciones de datos, actualizaciones de integridad
referencial en cascada o para hacer vistas de solo lectura actualizables.

Un aspecto importante a tener en cuenta con los módulos de PSQL es que no aportan control de
transacciones. Un módulo se ejecuta siempre en el ámbito de una transacción abierta previamente.
Desde el módulo no puede ser confirmada ni cancelada, lo único que se puede es lanzar una excepción
ante un problema detectado. La aplicación que ha llamado al módulo será la encargada de realizar la
finalización apropiada de la transacción (confirmar o deshacer).

En este tema veremos los ejemplos a partir de la base de datos de bancos:

Vicente Tejero Trueba Pag 1 IES Pedro Espinosa


Firebird: SQL Procedimental (PSQL) Tema 6

2.- EXTENSIONES DEL LENGUAJE.


Los módulos PSQL no son más que sentencias SQL que comienzan con una cláusula CREATE
y finalizan con un terminador. En la definición del módulo apareceran un número variable de
elementos como clausulas, palabras clave, bloques de sentencias, bucles, etc.

Un módulo de PSQL se define estableciendo dos elementos fundamentales:


- La cabecera: En ella se incluyen todos los elementos de definición del procedimiento o
trigger como nombre del módulo, parámetros de entrada y/o salida para los procedimientos,
tipo de trigger, etc.
- El cuerpo: En el se definen todos los elementos que desarrollan el módulo como la
declaración de variables, el bloque de sentencias (entre el BEGIN END) y los manejadores
de excepción.

Así tendríamos que en un procedimiento:


-- se inicia la cabecera
CREATE PROCEDURE procedimiento (entrada INTEGER) RETURNS (salida INTEGER)
--se acaba la cabecera
AS -- se inicia el cuerpo
DECLARE VARIABLE variable INT;
BEGIN
variable = entrada /2;
salida =variable;
END
-- fin del cuerpo

2.1.- Terminadores.
Muchos de los elementos que nos encontramos en un módulo se deben indicar con el
terminador ;. Ya que un módulo es una sóla sentencia finalizada con un terminador es necesario
diferenciar éste de los elementos del módulo. Esto se consigue mediante la sentencia SET TERM. Por
ejemplo una definición tipica de un procedimiento sería:

SET TERM ^; -- establece como terminador el ^ a partir de esta sentencia.


CREATE PROCEDURE MI_PROCEDIMIENTO
…….
AS
BEGIN

Vicente Tejero Trueba Pag 2 IES Pedro Espinosa


Firebird: SQL Procedimental (PSQL) Tema 6
sentencia; --sentencia acabada en ;
END ^ -- finaliza la definición del procedimiento mediante el terminador indicado, el ^

SET TERM ;^ -- fija de nuevo el terminador como ; y acaba la sentencia con el terminador actual ^

2.2.- Variables.
En PSQL se pueden usar 5 tipos de variables en el cuerpo de un módulo, con algunas
restricciones según sea procedimiento o trigger:
- Variables locales, usadas para mantener valores locales en un módulo.
- Las variables NEW.columna y OLD.columna usadas en los triggers para almacenar los
valores modificados de la fila o los antiguos.
- Variables de contexto.
- Argumentos de entrada pasados como constantes en los procedimientos almacenados.
- Argumentos de salida, usados para devolver valores en los procedimientos almacenados.

Las variables pueden usarse en sentencias SQL dentro de los módulos aunque en este caso se
preceden por ‘:’ para diferenciarlas de las columnas de las tablas.

……
FOR SELECT cod_accta FROM ACCIONISTAS
INTO :variable DO -- Variable lleva : al estar dentro de un select.
BEGIN
otra = otra + variable – variable no lleva los : al no aparecer en una sentencia select.
END
……

Una variable se asigna mediante el = ( variable = expresión), en donde se puede asignar a una
variable cualquier expresión en la que usemos variables, operadores, funciones SQL.

codigo = GEN_ID(‘generador’,1);
codigo = codigo + IIF(codigo<0,0,codigo);

Las variables locales se declaran, una por una, antes del primer BEGIN siendo su ámbito
únicamente el cuerpo del procedimiento o trigger. En la misma sentencia es posible darles un valor:

DECLARE VARIABLE variable


{<tipo>
| <dominio>
| TYPE OF <dominio>
| TYPE OF COLUMN <tabla o vista>.<columna>}
[{DEFAULT valor } | {= valor}];

DECLARE VARIABLE variable INTEGER DEFAULT 0;


DECLARE VARIABLE cadena CHAR(20) = ‘hola’;

En PSQL se pueden usar todas las variables de contexto y literales comentados en temas
anteriores como ‘NOW’, CURRENT_USER, etc. Además se define la variable ROW_COUNT que
contiene el número de filas afectadas por la última sentencia de DML ejecutada.

Los argumentos de entrada se definen en los procedimientos almacenados para que la


aplicación pueda pasar valores al mismo. Son declarados en la cabecera pudiéndose usar en cualquier
punto del cuerpo. Los argumentos de entrada son siempre pasados por valor, por lo que cualquier
cambio que se produzca dentro del cuerpo no se envía al exterior.

Vicente Tejero Trueba Pag 3 IES Pedro Espinosa


Firebird: SQL Procedimental (PSQL) Tema 6

A los argumentos de entrada se les puede asignar un valor por defecto lo que hace que se pueda
omitir en la llamada al procedimiento. En este caso, una ver definido uno, todos los siguientes deben
tener también un valor por defecto y omitirse en la llamada.

Los argumentos de salida se definen en los procedimientos almacenados para que puedan
devolver valores a la aplicación que los llama. Se declaran en la cabecera pudiéndose usar en cualquier
punto del cuerpo.

CREATE PROCEDURE procedimiento


[({arg_entrada <tipo> [= valor_defecto]}[, ……] )] – argumentos de entrada.
[RETURNS ({arg_salida <tipo>}[, ……] )] – argumentos de salida.
………

CREATE PROCEDURE procedimiento (entrada INTEGER=0) RETURNS (salida INTEGER)


AS
DECLARE VARIABLE variable INT;
BEGIN
variable = entrada /2;
salida =variable;
END

OLD y NEW son exclusivos de los triggers. OLD contiene los valores de la fila para la que se
activa el trigger antes de la modificación o borrado. NEW los valores de la fila modificados o
insertados. Estas variables se verán con más detenimiento con los triggers.

2.3.- Sentencias básicas.


En PSQL existe la sentencia bloque, es decir, aquella que permite definir en su interior otras
sentencias. En PSQL es BEGIN …. END. Se usa tanto para el bloque principal como para otros
internos.

En PSQL se trabaja con la sentencia condicional IF … THEN …. ELSE. Ésta tiene como
sintaxis:

IF (<condicion>)
THEN <sentencia compuesta>
[ELSE <sentencia compuesta>]

<sentencia compuesta> = {<bloque> | <sentencia>;}

La cláusula <condicion> es un predicado que se evalua a true o false. Si vale true se ejecuta la
sentencia del THEN. Si vale false se evalua, si existe, la sentencia del ELSE.

Podemos utilizar cualquier predicado. Por ejemplo podríamos tener:

IF (EXISTS (select * from accionistas where cod_accta=:cod)) THEN


var=’BUENA’;

IF (nombre in (‘PEDRO’,’JUAN’)) THEN


var= nombre;
ELSE

Vicente Tejero Trueba Pag 4 IES Pedro Espinosa


Firebird: SQL Procedimental (PSQL) Tema 6
var=’desconocido’;

En PSQL se define una sentencia de tipo bucle: WHILE … DO.

WHILE (<condicion>) DO
<sentencia compuesta>

<sentencia compuesta> = {<bloque> | <sentencia>;}

Por ejemplo podríamos tener, el siguiente procedimiento que suma todos los números desde el
0 hasta el que se indica como parámetro de entrada:

CREATE PROCEDURE SUMA (i INTEGER) RETURNS (su INTEGER)


AS
BEGIN
su=i;
i=i-1;
WHILE (i>0) DO
BEGIN
su=su+i;
i=i-1;
END
END

2.4.- Cursores.
Un cursor es un elemento que nos permite obtener los valores de una fila perteneciente a un
conjunto de salida obtenido a partir de una sentencia SELECT. En Firebird se trabaja tanto con
cursores explícitos, definidos explícitamente, como con cursores implícitos, aparecen por debajo de
una sentencia sin que nos demos cuenta.

En los cursores implícitos nos encontramos con:

- SELECT …. INTO …..: Se usa para asignar los campos de la única fila obtenida en una
sentencia a unas variables. Es semejante a una asignación en la que la expresión se obtiene
de la sentencia SELECT.

SELECT first 1 cod_accta, nom_accta FROM accionistas –sólo puede devolver una fila
INTO :var_codigo, :var_nombre -- se almacenan los campos en las variables indicadas

La sentencia SELECT debe devolver como máximo una sóla línea. Se deben indicar tantas
variables como campos se devuelvan.

- FOR SELECT: Se utiliza para recorrer una sentencia SELECT que devuelve una o más
filas realizando operaciones sobre los datos devueltos.

FOR SELECT <lista columnas>


FROM …….
INTO <lista variables> DO
BEGIN
<sentencias>
[SUSPEND;]

Vicente Tejero Trueba Pag 5 IES Pedro Espinosa


Firebird: SQL Procedimental (PSQL) Tema 6

END

En este caso se deben indicar tantas variables y del mismo tipo como columnas devuelva la
sentencia. Por ejemplo podemos tener un procedimiento que obtiene la máxima cotización
para un banco dado y actualiza el valor en la tabla bancos.

CREATE PROCEDURE ACT_PRE_MAX (banco INTEGER)


AS
DECLARE VARIABLE MAXIMO NUMERIC(12,2);
DECLARE VARIABLE VALOR NUMERIC(12,2);
BEGIN
MAXIMO =0;
FOR SELECT pre_ac_fe FROM cotizaciones
WHERE cod_banco=:banco
INTO :VALOR DO
BEGIN
IF (VALOR>MAXIMO) THEN
VALO=MAXIMO;
END
UPDATE BANCOS SET PRE_MA_AC=:MAXIMO
WHERE COD_BANCO=:BANCO;
END ^

- EXECUTE STATEMENT …. INTO: Es equivalente a SELECT INTO con la diferencia


de que aquí lo que se indica es una cadena que contiene la sentencia a ejecutar. Esto nos
permite definir sentencias dinámicas.

sentencia = ’select first 1 cod_banco from bancos’; --crea la sentencia en tiempo de ejecución
EXECUTE STATEMENT sentencia INTO :codigo; -- se ejecuta la sentencia almacenando el
resultado en una variable

Mediante esta sentencia se pueden ejecuta sentencias que no devuelven filas como
INSERT, UPDATE, DELETE, EXECUTE PROCEDURE y cualquier sentencia DDL
excepto las de CREATE/DROP DATABASE. Se tiene que tener en cuenta que dentro de
un módulo no es posible ejecutar sentencias DDL directamente.

Por ejemplo para ejecutar un procedimiento que se pasa como parámetro podríamos tener:
CREATE PROCEDURE EJECU_PROCE(PROCE VARCHAR(30))
AS
DECLARE VARIABLE SENTENCIA VARCHAR(1000);
BEGIN
SENTENCIA = ‘EXECUTE PROCEDURE ‘ || PROCE;
EXECUTE STATEMENT SENTENCIA;
END^

- FOR EXECUTE STATEMENT: Al igual que FOR SELECT permite recorrer las filas
devueltas al ejecutar una sentencia.

FOR EXECUTE STATEMENT sentencia


INTO <lista variables> DO
BEGIN
<sentencias>
END

Vicente Tejero Trueba Pag 6 IES Pedro Espinosa


Firebird: SQL Procedimental (PSQL) Tema 6

Cuando usemos esta sentencia tenemos que tener en cuenta que debemos garantizar que la
cadena que generemos sea correcta, puesto que, a diferencia de FOR SELECT, la sentencia
no es validada en el momento de definir el procedimiento. También tenemos que tener en
cuenta que la sentencia se ejecutará de forma má lenta ya que tiene que ser compilada cada
vez que se vaya a ejecutar.

Por ejemplo si queremos crear un procedimiento que concatene un campo cadena de una
tabla podríamos tener:

CREATE PROCEDURE CONCATENAR (TABLA VARCHAR(30), CAMPO VARCHAR(30))


RETURNS (CADENA VARCHAR(2000))
AS
DECLARE VARIABLE SENTENCIA VARCHAR(1000);
DECLARE VARIABLE VALOR VARCHAR(1000);
BEGIN
CADENA=’’;
SENTENCIA = ‘SELECT ‘ || CAMPO || ‘FROM ‘ || TABLA;
FOR EXECUTE STATEMENT SENTENCIA
INTO :VALOR DO
BEGIN
CADENA = CADENA || ‘, ‘+VALOR;
END
END ^

Mediante la sentencia FOR SELECT es posible definir los procedimientos almacenados


seleccionables, es decir, aquellos que pueden actuar como tablas en sentencias SELECT, en
contraposición con los procedimientos almacenados ejecutables que son aquellos que ejecutan una
operación programada.
Un procedimiento almacenado ejecutable se obtiene con la inclusión de una sentencia
SUSPEND dentro del bucle DO. Esto causa que, una vez asignadas las variables correspondientes al
cursor, el bucle se pare hasta que la fila sea recogida por el servidor.

Por ejemplo si queremos un procedimiento que devuelva un nombre propio para cada banco
podríamos tener:

CREATE PROCEDURE MIS_BANCOS () RETURNS (COD_BANCO INT, MI_NOMBRE


VARCHAR(100))
AS
BEGIN
FOR SELECT COD_BANCO,NOM_BANCO
FROM BANCOS
INTO :COD_BANCO, MI_NOMBRE DO
BEGIN
MI_NOMBRE=’EL BANCO ES ‘ || MI_NOMBRE;
SUSPEND;
END
END ^

Se podría llamar

select * from MIS_BANCOS()

Los cursores explícitos son aquellos que se definen de forma explícita mediante una sentencia
de declaración. En Firebird nos encontramos con las siguientes sentencias de gestión de cursores:

Vicente Tejero Trueba Pag 7 IES Pedro Espinosa


Firebird: SQL Procedimental (PSQL) Tema 6

DECLARE [VARIABLE] <nombre cursor> CURSOR FOR ( <sentencia select> );


OPEN < nombre cursor >;
FETCH < nombre cursor > INTO <variable> [, <variable> ...];
CLOSE < nombre cursor >;

Con los cursores explícitos se tienen que tener una serie de consideraciones:
- Dos cursores no pueden tener el mismo nombre aunque si un cursor y una variable. Los
nombres de cursores deben ser únicos.
- Intentar hacer un fetch (devolver los valores para la fila actual) en un cursor no abierto da
un error.
- Se usa ROW_COUNT para comprobar si fetch devuelve o no filas.

Por ejemplo el procedimiento MIS_BANCOS mediante cursores podría ser:

CREATE PROCEDURE MIS_BANCOS () RETURNS (COD_BANCO INT, MI_NOMBRE


VARCHAR(100))
AS
DECLARE MI_CURSOR CURSOR FOR SELECT COD_BANCO,NOM_BANCO
FROM BANCOS –declaro el cursor para la sentencia
BEGIN

OPEN MI_CURSOR; -- abro el cursor.


FETCH MI_CURSOR INTO :COD_BANCO, MI_NOMBRE; --obtengo los valores para la fila
WHILE (ROW_COUNT>0) DO
BEGIN
MI_NOMBRE=’EL BANCO ES ‘ || MI_NOMBRE;
SUSPEND;
FETCH MI_CURSOR INTO :COD_BANCO, MI_NOMBRE;
END
CLOSE MI_CURSOR; -- cierro el cursor.
END ^

2.5.- Sentencias de control de flujo.


Firebird proporciona varias sentencias que afectan al control de flujo:
- SUSPEND: ya se ha comentado anteriormente.
- EXIT: Hace que se vaya al final de un procedimiento.
- END: Finaliza la ejecución.

Según se tenga un procedimiento ejecutable, seleccionable o un trigger estas sentencias pueden


trabajar con ciertos matices:

MODULO SUSPEND EXIT END


Procedimiento Se suspende la ejecución Devuelve los Devuelve el control a la aplicación
seleccionable hasta que la aplicación valores (si los y pone SQLCODE a 100
solicite un nuevo registro hay) y salta al
al procedimiento END final
Procedimiento Salta al END final (no se Salta al END Devuelve los valores y pasa el
ejecutable aconseja) final control a la aplicación.
trigger No se usa Salta al END Hace que se ejecute el siguiente
final trigger en la misma fase (AFTER o
BEFORE)si hay. En otro caso
termina el trigger.

Vicente Tejero Trueba Pag 8 IES Pedro Espinosa


Firebird: SQL Procedimental (PSQL) Tema 6

Además de los anteriores se tiene la sentencia LEAVE. Se usa en los bucles para salir de ellos.
Hace que se pase a la siguiente sentencia fuera del bucle.

CONT=0;
WHILE (1=1) DO -- esta codición supone un bucle infinito.
BEGIN
CONT=CONT+1;
IF (CONT>9) THEN
LEAVE; -- solo se puede salir mediante LEAVE.
END
…….. – cuando se ejecuta LEAVE se va a la siguiente sentencia

2.6.- Ejecución de código.


En PSQL es posible ejecutar directamente procedimientos almacenados, sentencias y más
recientemente bloques de código.

Un procedimiento almacenado se ejecuta mediante la sentencia EXECUTE PROCEDURE.

EXECUTE PROCEDURE procedimiento [(] [argumento [, ….]] [)]


[RETURNING_VALUES variable[,…];

Esta sentencia permite llamar a un procedimiento almacenado indicando valores para los
argumentos de entrada. Si se definen argumentos de salida se pueden devolver los valores en las
variables indicadas.

EXECUTE PROCEDURE suma 5 RETURNING_VALUES :resultado; -- se llama al procedimiento


suma visto en ejemplos anteriores.

Recientemente Firebird ha incluido la sentencia EXECUTE BLOCK. Esta sentencia no es


realmente una sentencia de PSQL aunque hace que un ‘PSQL dinámico’ esté disponible en una
sentencia SELECT. Tiene el efecto de que un bloque de código de PSQL se pueda ejecutar en una
sentencia de DSQL como si fuera un procedimiento almacenado.

Su sintaxis es:
EXECUTE BLOCK [ (parámetro tipo = ?[, parametro tipo = ? ...) ]
[ RETURNS (parametro tipo[, param datatype ...]) ]
AS
[DECLARE VARIABLE var datatype; ...]
BEGIN
...
END

2.7.- Eventos.
Los eventos son un mecanismo por el que desde Firebird se pueden enviar señales a las
aplicaciones para notificar cualquier suceso de forma asíncrona, es decir, sin que la aplicación tenga
que estar a la espera.

Se envían eventos mediante:

Vicente Tejero Trueba Pag 9 IES Pedro Espinosa


Firebird: SQL Procedimental (PSQL) Tema 6

POST_EVENT evento;

Vicente Tejero Trueba Pag 10 IES Pedro Espinosa


Firebird: SQL Procedimental (PSQL) Tema 6

3.- PROCEDIMIENTOS ALMACENADOS


Un procedimiento almacenado es un módulo escrito en PSQL, compilado por el interprete de
lenguaje binario de Firebird y almacenado como código ejecutable en los metadatos de la base de
datos. Una vez compilado, el procedimiento puede ser invocado directamente desde una aplicación u
otro módulo PSQL usando EXECUTE PROCEDURE o una sentencia SELECT.

Se distinguen dos tipos de procedimientos almacenados:


- Ejecutables: Son los procedimientos que se llaman con la sentencia EXECUTE
PROCEDURE y que pueden devolver, opcionalmente, una única fila de uno o más valores.
Se usan a menudo para realizar operaciones de inserción, actualización o borrado, o para
larzar un conjunto de operaciones.
- Seleccionable: Los procedimientos seleccionables son escritos con extensiones del
lenguaje para generar un conjunto de salida de múltiples filas que es devuelto usando una
sentencia SELECT.

En principio no se hace distinción entre procedimiento ejecutable o seleccionable. Cuando son


llamados, y no se ajustan a la forma de llamada, el sistema genera una excepción. Es por tanto función
del programador garantizar que se usan de la forma apropiada.

Un procedimiento se crea mediante la sentencia CREATE PROCEDURE:

CREATE PROCEDURE procedimiento


[(argumento tipo[{= | DEFAULT} valor]
[, argumento tipo[{= | DEFAULT} valor]…)]
[RETURNS (argumento tipo [, argumento tipo…])]
AS – aquí empieza el cuerpo
[DECLARE [VARIABLE] variable tipo[{= | DEFAULT} valor];
BEGIN
<sentencias>
END <terminador>

Como se ve se pueden indicar una lista de argumentos de entrada a los que se les pueden
asignar valores por defecto. En este caso todos los siguientes argumentos de entrada deben tener
también definidos valores por defecto.

Se puede indicar uno o mas argumentos de salida mediante la cláusula RETURNS.

Se pueden declarar variables antes del bloque principal.

Por ejemplo podríamos tener

CREATE PROCEDURE PRUEBA (entrada integer = 10) – se define un argumento de entrada con
valor por defecto 10
RETURNS (salida integer) -- se define un argumento de salida
AS
DECLARE VARIABLE NUEVA INTEGER=3; -- declaramos una variable local
BEGIN
/* Procedure body */
nueva=nueva+entrada+5; -- usamos las variables locales y los argumentos
salida=nueva; -- se rellena un argumento de salida
SUSPEND; -- se envia la fila a la aplicación que lo llame.
END

Vicente Tejero Trueba Pag 11 IES Pedro Espinosa


Firebird: SQL Procedimental (PSQL) Tema 6

El anterior procedimiento se podría llamar como:

select * from prueba – se llama como procedimiento seleccionable sin rellenar los argumentos
select * from prueba(5) – se llama al procedimiento seleccionable indicando el argumento

o como procedimiento ejecutable dentro de otro

…..
AS
DECLARE VARIABLE varia integer;
BEGIN
…….
execute procedure prueba(3) RETURNING_VALUES (varia);
…….
END

En este caso la llamada al procedimiento ha funcionado tanto como seleccionable como


ejecutable ya que devolvía como máximo una fila. Si el procedimiento se hubiera definido para que
devolviera mas de una fila (Sentencia suspend dentro de un for select ,… ) nos habría dado una
excepción al llamarlo mediante EXECUTE PROCEDURE.

Una ventanja de los procedimientos almacenados es que se pueden definir de forma recursiva,
es decir, se pueden llamar a sí mismos.

En Firebird se establecen varios mecanismos para modificar un procedimiento almacenado:

{CREATE | ALTER | RECREATE | CREATE OR ALTER} PROCEDURE procedimiento


[(argumento tipo[{= | DEFAULT} valor]
[, argumento tipo[{= | DEFAULT} valor]…)]
[RETURNS (argumento tipo [, argumento tipo…])]
AS – aquí empieza el cuerpo
[DECLARE [VARIABLE] variable tipo[{= | DEFAULT} valor];
BEGIN
<sentencias>
END <terminador>

ALTER es el método de más bajo imparto para cambiar un procedimiento ya que, si ho hay
dependencias que se vean afectadas, se puede ejecutar sin problemas. Para que se pueda ejecutar debe
existir previamente el procedimiento.

RECREATE es idéntico a CREATE con la adicción de que previamente hace un DROP


PROCEDURE. En este caso se producirán excepciones si existen dependencias (no se puede borrar el
objeto si antes no se eliminan las dependencias).

CREATE OR ALTER crea un procedimiento si no existe. Si ya existe ejecuta una operación


ALTER.

Un procedimiento se borra mediante DROP PROCEDURE procedimiento. Un procedimiento


sólo lo puede borrar el usuario SYSDBA o el propietario del procedimiento. Además, para que se
pueda realizar, no puede estar siendo usado en ninguna transacción ni tener dependencias en otros
objetos de la base de datos. Si el procedimiento es recursivo se necesita borrar en primer lugar la
llamada a sí mismo.

Vicente Tejero Trueba Pag 12 IES Pedro Espinosa


Firebird: SQL Procedimental (PSQL) Tema 6

4.- TRIGGERS
Un trigger o disparador es un módulo que se ejecuta de forma automática cuando se cambia el
estado de una fila como respuesta a una petición. Los triggers no se pueden invocar por aplicaciones u
otros procedimientos. Además, no pueden tener argumentos de entrada o salida.

Los triggers se ejecutan a nivel de fila, una por cada imagen de fila cambiada y se establece un
alto grado de granularidad en cuanto a cuando, en que orden condiciones se dispara un trigger
particular. Esto se establece mediante la fase, evento y secuencia.

Un trigger se puede ejecutar en una de dos fases relativas a la ejecución de la petición de


cambio realizada: antes (befote) de escribir o después (alter). En un trigger es posible definir esta fase
en relación a un evento (inserción, actualización o borrado) o a varios de ellos.

En resumen tenemos que:

Tipo trigger Descripción


BEFORE INSERT Se lanza antes de que se cree una nueva fila. Permite que se puedan
cambiar los valores de entrada
AFTER INSERT Se dispara después de que se cree una nueva versión del registro. No se
permite cambiar los valores de entrada. Normalmente se usa para
modificar otras tablas
BEFORE UPDATE Se dispara antes de que se cree una nueva versión del registro. Se permiten
cambios en los valores de entrada
AFTER UPDATE Se lanza después de que se cree una nueva versión del registro. No se
permiten cambios en los valores de entrada. Normalmente se usa para
modificar otras tablas
BEFORE DELETE Se lanza antes de que una fila existente se borre. No acepta cambios a
ninguna columna en la fila.
AFTER DELETE Se dispara después de que una fila es borrada. Nose aceptan cambios a
ninguna columna en la fila . Normalmente se usa para modificar otras
tablas.
BEFORE <evento> OR Se dispara antes de que se ejecute cualquier cambio en el estado del dato.
<evento> [OR Se codifican las acciones de forma condicional. Se pueden hacer cambios
<evento>] en las columnas de la fila afectada salvo que sea una acción de borrado
AFTER <evento> OR Se dispara después de que se ejecute cualquier cambio en el estado del
<evento> [OR dato. Se codifican las acciones de forma condicional. No se pueden hacer
<evento>] cambios de la fila afectada.

Firebird permite ejecutar múltiples triggers para cada combinación fase/evento. Para establecer
el orden en el que se ejecutan se define un número de secuencia. Por defecto vale 0. Si se indican
números de secuencia se ejecutan los triggers en orden ascendente. Cuando varios triggers tienen
definidos el mismo número de secuencia, se establece un orden alfabético por sus nombres.

Un trigger puede estar activo o inactivo. Sólo se disparan los triggers activos. El estado se
puede modificar mediante la sentencia ALTER.

Un trigger se crea mediante la sentencia CREATE TRIGGER:

CREATE TRIGGER nombre FOR {tabla | vista}


[ACTIVE | INACTIVE ]

Vicente Tejero Trueba Pag 13 IES Pedro Espinosa


Firebird: SQL Procedimental (PSQL) Tema 6

{BEFORE |AFTER }
{{DELETE | INSERT | UPDATE }
| { DELETE OR {[INSERT [OR UPDATE]} | {INSERT OR […]} | {UPDATE OR
[…]}}}
[POSITION numero]
AS – aquí empieza el cuerpo del trigger
[DECLARE [VARIABLE] variable tipo[{= | DEFAULT} valor];
BEGIN
<sentencias>
END <terminador>

Si usamos la sintaxis BEFORE INSERT OR DELETE OR UPDATE, se puede usar, dentro del
cuerpo, las variables de contexto INSERTING, UPDATING y DELETING que indican ante que tipo
de evento se está respondiendo.

Además de las variables anteriores, PSQL aporta las variables OLD y NEW. Contienen
respectivamente los valores de las columnas existentes y los nuevos. No siempre están disponibles.
Por ejemplo ante un evento DELETE, solo tenemos valores OLD mientras que ante un INSERT solo
tenemos NEW.

Los triggers se usan para proporcionar valores por defecto, validar y transformar entradas del
usuario, actualizar otras tablas relacionadas o para garantizar reglas de integridad.

CREATE TRIGGER BI_BANCOS


FOR BANCOS
BEFORE INSERT
AS
BEGIN
new.nom_banco=upper(new.nom_banco); -- pasamos a mayúsculas el nombre de banco
END

Un uso muy común de los triggers es implementar las columnas autonuméricas haciendo uso
de un generador. Por ejemplo si tubieramos un generador llamado “gen_cod_banco” para generar
codigos de bancos para la tabla bancos podríamos tener el trigger:

CREATE TRIGGER BI_BANCOS


FOR BANCOS
BEFORE INSERT
AS
BEGIN
IF (new.cod_banco is null) then – si no se ha indicado un cod_banco
new.cod banco=gen_id(gen_cod_banco,1); --se recoge el cod_banco del generador
END

Un trigger puede ser modificado mediante las sentencia ALTER TRIGGER y CREATE OR
ALTER TRIGGER. Ambas tienen una sintaxis semejante a la de CREATE TRIGGER con la única
diferencia que en ALTER TRIGGER no se indica la tabla sobre la que se trabaja:

{{CREATE TRIGGER nombre}


| {CREATE OR ALTER TRIGGER nombre FOR {tabla | vista}}}
[ACTIVE | INACTIVE ]
{BEFORE |AFTER }
{{DELETE | INSERT | UPDATE }
……..

Vicente Tejero Trueba Pag 14 IES Pedro Espinosa


Firebird: SQL Procedimental (PSQL) Tema 6

Los triggers se borran mediante la sentencia DROP TRIGGER nombre;

Vicente Tejero Trueba Pag 15 IES Pedro Espinosa


Firebird: SQL Procedimental (PSQL) Tema 6

5.- EXCEPCIONES
PSQL aporta un mecanismo para la gestión de errores. De forma estándar los módulos
gestionan las excepciones parando la ejecución, deshaciendo el trabajo hecho desde la sentencia
BEGIN inicial y pasando uno o más mensajes de error.

Una excepción puede ocurrir por:


- Error SQL: Mensajes SQL con código negativo de SQLCODE. Además se tienen otros
códigos SQLCODE que no representan error como el 0 (ejecutado correctamente) o el 100
(detectado fin de fichero).
- Errores internos de Firebird: Tienen que ver con concurrencia, datos, metadatos y
condiciones del entorno. Tienen un código de error de nueve dígitos, normalmente
empezando por 3355, que identifica de forma única el GDSCODE. Un mismo SQLCODE
puede ser descompuesto en varios GDSCODE de forma que éte último nos da una
información mas precisa sobre el error producido. Cuando ocurre una excepción se pueden
trabajar muchas veces con ambos: SQLCODE y GDSCODE. Por ejemplo podríamos tener
el GDSCODE 335544747 usrname_too_long, el 335544768 password_too_long,
335544749 usrname_required, etc , todos bajo el mismo SQLCODE, el -85.
- Excepciones de usuario: Son objetos creados en la base de datos y que se invocan cuando
se cumple una condición determinada.

Como se ve , una excepción no es más que un mensaje que se genera cuando se produce un
error. Todas las excepciones predefinidas (SQLCODE y GDSCODE) tiene mensajes de texto
asociados.

Una excepción definida por el usuario se crea mediante CREATE EXCEPTION.

CREATE EXCEPTION <nombre> <mensaje>;

En donde nombre es un identificador de cómo máximo 31 caracteres y mensaje una cadena con
conjunto de caracteres NONE. Por ejemplo podríamos tener:

CREATE EXCEPTION BANCO_ERRONEO ‘El nombre del banco no válido’;

Se puede borrar una excepción (DROP EXCEPTION <nombre>) o modificar una existente
(ALTER EXCEPTION <nombre> <mensaje>).

Las excepciones definidas internamente son lanzadas por el sistema en respuesta a errores que
requieren que se pare la ejecución. Éstas cubren un gran rango de condiciones incluyendo violaciones
de restricciones de integridad, desbordamientos aritméticos y de cadena, referencias a objetos no
existentes, corrupción de datos, etc. Las excepciones GDSCODE y SQLCODE son las mismas que
nos encontramos cuando se produce un error al ejecutar una sentencia de DSQL.

Las excepciones de usuario, sólo disponibles en módulos PSQL, son usadas para codificar
nuestros propios errores en la lógica de los programas.

De esta forma podríamos encontrarnos con un trigger usado para validar el nombre de un banco
insertado o modificado:

CREATE TRIGGER BI_BANCOS FOR BANCOS – trigger para validar el nombre del banco
BEFORE INSERT OR UPDATE

Vicente Tejero Trueba Pag 16 IES Pedro Espinosa


Firebird: SQL Procedimental (PSQL) Tema 6
AS
BEGIN
IF (NEW.NOM_BANCO NOT CONTAINING ‘BANCO’) THEN – si el nombre no cumple la
condición
EXCEPTION BANCO_ERRONEO; – lanzo la excepción de usuario.
END ^

Como se ve en este ejemplo, se puede lanzar una excepción mediante la sentencia


EXCEPTION:

EXCEPTION [<nombre excepcion> [<mensaje en tiempo ejecución>]]

Si usamos esta sentencia sin ningún parámetro se puede relanzar la excepción para que la
gestione otro bloque. Otro uso posible es la de asignarle el mensaje directamente en tiempo de
ejecución.

Además de poder lanzar nuestras propias excepciones Firebird aporta un mecanismo para
gestionar, dentro de un módulo, las excepciones generadas y así poder continuar con la ejecución de
nuestro código. Esto se consigue mediante la sentencia WHEN:

WHEN {<nombre excepcion> | GDSCODE codigo | SQLCODE codigo | ANY } DO


BEGIN
<sentencias>
END

Mediante la sentencia WHEN se cambia el mecanismo estándar de gestión de errores. Asi


cuando se produce una excepción se para la ejecución del bloque en el que se produce. En lugar de
pasar la ejecución a la sentencia END final, se inicia un procedimiento de búsqueda de una sentencia
WHEN que gestione la excepción, comenzando por el bloque donde se produjo el error y pasando a
los bloques más externos. Una vez manejada la excepción se continua con la ejecución en la primera
sentencia posterior al bloque de manejo de excepción ejecutado.

Una sentencia WHEN siempre se tiene que ubicar inmediatamente antes de una sentencia END
(no puede haber otras sentencias entre ellas).

CREATE TRIGGER BI_BANCOS FOR BANCOS – trigger para validar el nombre del banco
BEFORE INSERT OR UPDATE
AS
BEGIN
IF (NEW.NOM_BANCO NOT CONTAINING ‘BANCO’) THEN – si el nombre no cumple la
condición
EXCEPTION BANCO_ERRONEO; – lanzo la excepción de usuario.

WHEN BANCO_ERRONEO DO – capturo la excepcion y la gestiono.


BEGIN
NEW.NOM_BANCO = ‘BANCO ‘ || NEW.NOM_BANCO;
END
END ^

Firebird aporta dos variables de contexto para poder consultar el error producido: SQLCODE y
GDSCODE. Estas variables se asignan de forma automática con el código de error permaneciendo en
el bloque de gestión de error. Fuera de este bloque siempre valen 0.

Vicente Tejero Trueba Pag 17 IES Pedro Espinosa

También podría gustarte