Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Tema 6 - PSQL
Tema 6 - PSQL
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
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.
- 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).
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 ;^ -- 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:
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.
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.
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.
En PSQL se trabaja con la sentencia condicional IF … THEN …. ELSE. Ésta tiene como
sintaxis:
IF (<condicion>)
THEN <sentencia compuesta>
[ELSE <sentencia compuesta>]
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.
WHILE (<condicion>) DO
<sentencia compuesta>
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:
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.
- 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.
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.
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.
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:
Por ejemplo si queremos un procedimiento que devuelva un nombre propio para cada banco
podríamos tener:
Se podría llamar
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:
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.
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
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.
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.
POST_EVENT evento;
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.
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
select * from prueba – se llama como procedimiento seleccionable sin rellenar los argumentos
select * from prueba(5) – se llama al procedimiento seleccionable indicando el argumento
…..
AS
DECLARE VARIABLE varia integer;
BEGIN
…….
execute procedure prueba(3) RETURNING_VALUES (varia);
…….
END
Una ventanja de los procedimientos almacenados es que se pueden definir de forma recursiva,
es decir, se pueden llamar a sí mismos.
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.
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.
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.
{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.
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:
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:
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.
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.
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:
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
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:
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.
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.