T2 PLPGSQL
T2 PLPGSQL
postgresql pl/pgsql
LECCIÓN 2: EL LENGUAJE PL/PGSQL EN POSTGRESQL
Autores:
Introducción
SQL es un lenguaje estándar para realizar consultas a un servidor de base de datos, quien ejecuta
cada sentencia de manera individual. Ello implica que las aplicaciones cliente deben enviar cada
consulta al servidor, esperar sea procesada, recibir los resultados, manejar los datos y después
enviar la siguiente sentencia.
Al usar PL/pgSQL es posible realizar cálculos, manejo de cadenas y consultas dentro del servidor
de la base de datos, combinando el poder de un lenguaje procedural y la facilidad de uso de SQL,
minimizando el tiempo de conexión entre el cliente y el servidor.
Este lenguaje es soportado por el Sistema Gestor de Bases de Datos PostgreSQL. Dentro de sus
potencialidades se incluye la posibilidad de agrupar bloques de sentencias con una funcionalidad
específica dentro de objetos tipo funciones.
En esta lección se estudiarán los comandos para la creación de funciones y los elementos que la
componen. Además, aprenderá cómo dotar al SGBD de un comportamiento más activo que nos
permitirá controlar de forma más efectiva el manejo de los datos y las sentencias DML
involucradas.
Durante esta Lección se observan algunos códigos generales que emplean el uso de
[corchetes] para resaltar los elementos opcionales dentro de las sintaxis.
Además, [,…] asociado a la posibilidad de repetir o adicionar elementos con el mismo fin
dentro de la sintaxis.
Siempre estarán en color azul las palabras reservadas del lenguaje SQL.
• Antes de adentrarse en la Lección usted debe haber adquirido con anterioridad los
conocimientos asociados al Tema 1:
o Lenguajes DDL y DML
o Tipos de datos
Como se observa en el ejemplo anterior, todas las variables, filas y registros usados en un bloque
o en sus sub-bloques deben declararse en la sección DECLARE del bloque, a excepción de las
variables de un ciclo FOR (observar más adelante) que itera sobre un rango de valores enteros.
Las variables en PL/pgSQL pueden ser de cualquier tipo de datos de los estudiados en el Tema 1
y su valor por omisión es el valor NULL de SQL.
En PostgreSQL se pueden definir constantes y su declaración tiene la siguiente sintaxis:
nombre [ CONSTANT ] tipo [ NOT NULL ] [ { DEFAULT | := } valor ]
El valor de una variable declarado como CONSTANT no puede ser modificado. Si acaso se
especifica NOT NULL, la asignación de un valor NULL causa un error en tiempo de ejecución.
Otros ejemplos de declaraciones de variables son:
cantidad INTEGER := 32;
url VARCHAR := 'http://misitio.com';
user_id CONSTANT INTEGER := 10;
En la declaración de variables también es posible copiar el tipo de datos de una fila o estructura
de otro objeto de la base de datos. Por ejemplo si tiene una tabla que guarda la información de las
personas, ejemplo el salario y no sabe si su tipo de dato es entero (INTEGER) o decimal
(DOUBLE), para declarar otra variable del mismo tipo puede usar %TYPE:
c_salario Persona.salario%Type;
Si en cambio, desea declarar una variable del mismo tipo de una fila de la tabla Persona, se puede
emplear %ROWTYPE:
pers Persona%ROWTYPE;
Para asignarle un valor fijo a una variable, ejemplos anteriores, basta con usar := valor. Sin
embargo, una asignación de una selección (SELECT) completa en un registro o fila puede hacerse
del siguiente modo:
SELECT campos INTO variables FROM …
En esta sentencia se ponen los campos a obtener y las variables que recibirán los valores, ambos
separados por coma (,). Se debe tener en cuenta los tipos de datos de los campos que se
devuelven y los tipos de las variables que lo reciben. Al usar esta sintaxis, existe una variable
especial llamada FOUND de tipo booleano, que puede usarse inmediatamente después de
SELECT .. INTO .. para comprobar si una asignación ha tenido éxito.
El empleo de la variable FOUND está ligado al uso de estructuras condiciones que tienen la forma
conocida de otros lenguajes:
IF expresión_booleana THEN
sentencias
[ELSE sentencias]
END IF;
En la sintaxis de la excepción pueden ser usados varios símbolos (%), siempre indicando sus
respectivos valores separados por comas en el mismo orden de aparición. También se pueden
emitir notificaciones (RAISE NOTICE) las cuales no detienen la ejecución del código.
Otra de las estructuras que son permitidas dentro de un bloque son las repetitivas o bucles,
utilizadas para iterar sobre un conjunto de datos determinados y hacer alguna operación con ellos.
Existen varias definiciones de bucles:
LOOP
sentencias Bucle no condicional que ha de ser terminado de forma explícita,
EXIT WHEN condición mediante una sentencia EXIT.
END LOOP;
WHILE condición
LOOP Bucle condicional que se ejecuta mientras la evaluación de
sentencias expresión sea cierta.
END LOOP;
Bucle que itera sobre un rango de valores enteros. La variable se
crea automáticamente de tipo entero, y existe solo dentro del
FOR variable IN [REVERSE] bucle. L1 y L2 dan el límite inferior y superior del rango y son
L1 .. L2 BY [expresión] evaluados solo cuando se entra en el bucle. REVERSE, si es
LOOP especificado, invierte los límites inferiores y máximo del bucle. El
sentencias valor por defecto del paso de la iteración es siempre 1, no
END LOOP; obstante puede ser modificado utilizando el comando BY
[expresión], el valor asignado a expresión definiría el nuevo paso
de la iteración.
FOR variable IN sentencia_SELECT
A la variable se le asigna todas las filas resultantes de la cláusula
LOOP
de SELECT, y las sentencias se ejecutan para cada una de ellas.
Sentencias
Si el bucle se termina con una sentencia EXIT, la última fila
[EXIT WHEN condición]
asignada es aún accesible después del bucle.
END LOOP;
El siguiente ejemplo devuelve los números pares del 0 al 10, para las tres primeras variantes de
bucles. La cuarta variante será expuesta en los ejemplos del siguiente apartado.
DO $$ DO $$
DECLARE DECLARE
cont INTEGER := 0; cont INTEGER := 0; DO $$
BEGIN BEGIN BEGIN
LOOP WHILE cont<10 FOR cont IN 0..10 BY 2 LOOP
cont:=cont+2; LOOP RAISE NOTICE '%', cont;
RAISE NOTICE '%', cont; cont:=cont+2; END LOOP;
EXIT WHEN cont = 10; RAISE NOTICE '%', cont; END; $$
END LOOP; END LOOP;
END; $$ END; $$
FUNCIONES
Estos objetos están programados con un lenguaje procedural específico. Pueden facilitar la
administración de la base de datos y la visualización de la información contenida en ella. Una
función, se puede ejecutar desde una aplicación, posibilita el uso de variables, funciones internas
del SGBD, así como estructuras para implementar ejecuciones condicionales y ciclos. En las
funciones son válidas las sentencias del DDL (CREATE, ALTER, DROP).
Ventajas
• Posibilita ejecutar una serie de instrucciones SQL en una única función.
• Permite realizar llamadas a otras funciones, simplificando instrucciones complejas.
• Se compila en el servidor al ser creada; por tanto, se ejecuta con mayor rapidez que las
instrucciones SQL individuales.
• Al estar almacenadas en el servidor de BD tiene acceso directo a los datos y sólo necesita
enviar el resultado final al usuario.
• Permiten que la lógica del negocio se encuentre almacenada en la base de datos, simplificando
la gestión de los datos. Se reduce la probabilidad de que los datos sean corrompidos por el
uso de programas clientes defectuosos o erróneos.
Desventajas
• Conlleva a los desarrolladores a incluir la lógica de negocio al SGBD, lo cual no es muy
aconsejable, puesto que se crea una alta dependencia del SGBD.
Una función en el lenguaje procedural por lo general tiene la sintaxis siguiente:
CREATE [OR REPLACE] FUNCTION nombre_función ([lista_parámetros]) RETURNS tipo_retorno
AS
$$
[BLOQUE]
[RETURN expresión]
$$
LANGUAGE 'plpgsql';
El acceso a los parámetros, puede ser mediante el símbolo $ seguido de un número que denota
la posición del mismo o a partir de sus nombres como en otros lenguajes conocidos.
Por ejemplo: $1 indica hacia el 1er parámetro pasado y $3 indica hacia el 3ero.
CURSORES
Una de las estructuras de control de datos que puede ser empleada como parte de una función
son los conocidos cursores. Estos permiten operar sobre los registros resultantes de una consulta
SELECT.
De manera general los cursores amplían el procesamiento de los resultados de una consulta
porque:
• Permiten situarse en filas específicas del conjunto de resultados.
• Recuperan una fila o bloque de filas de la posición actual en el conjunto de resultados.
• Aceptan modificaciones de los datos de las filas en la posición actual del conjunto de
resultados.
• Aceptan diferentes grados de visibilidad para los cambios que realizan otros usuarios en la
información de la base de datos que se presenta en el conjunto de resultados.
Los cursores se utilizan principalmente en procedimientos almacenados o funciones,
desencadenadores y secuencias de comandos de SQL, a fin de dejar disponible el contenido de
un conjunto de resultados para otras instrucciones SQL.
Los cursores se procesan en 4 pasos:
1. Declarar el cursor: como parte del DECLARE de la función para crear una variable de este
tipo solo se debe utilizar una de las siguientes sintaxis:
nombre_cursor CURSOR FOR (SELECT … FROM … ORDER BY …);
o
nombre_cursor refcursor;
Las dos sintaxis declaran cursores del tipo de datos refcursor. Sin embargo, la primera crea un
cursor específico para la consulta asociada, mientras que con la segunda este puede ser usado
con cualquier consulta que se le asocie cuando sea abierto.
2. Abrir el cursor: una vez inicie la función (BEGIN) en dependencia del tipo de declaración se
abrirá empleado:
OPEN nombre_cursor;
o
OPEN nombre_cursor FOR (SELECT … FROM … ORDER BY …);
Abrir el cursor significa reservar memoria suficiente para el cursor, ejecutar la sentencia
SELECT a la que se refiere el cursor y colocar el puntero antes de la primera fila.
3. Procesar el cursor. para esta tarea, se emplean las instrucciones FETCH y MOVE, ambas
permiten colocar el cursor en una posición y recorrerlo en una dirección definida, registro a
registro hasta que el puntero llegue al final (hasta que el cursor esté vacío). La diferencia radica
en que FETCH permite acceder los datos desde una variable, mientras que MOVE solo permite
conocer la posición.
FETCH [ dirección { FROM | IN } ] nombre_cursor INTO variable;
Cada vez que se ejecuta el comando FETCH, se obtiene la próxima fila correspondiente en el
sentido que se especifica en la opción dirección y su contenido lo almacena en variables. Por
ello se requiere usar bucles cuando se desea procesar un conjunto de filas de la consulta
asociada al cursor.
Al emplear la sentencia MOVE
MOVE [ dirección { FROM | IN } ] nombre_cursor;
se puede referenciar la posición del cursor con la fila de la tabla de donde proviene, para
eliminar o actualizar los datos, empleando la opción CURRENT OF nombre_cursor, por
ejemplo:
DELETE FROM nombre_tabla WHERE CURRENT OF nombre_cursor;
UPDATE nombre_tabla SET columna=expresión WHERE CURRENT OF nombre_cursor;
La opción dirección indica la próxima fila a seleccionar y su valor puede ser: NEXT, PRIOR,
FIRST, LAST, ABSOLUTE cantidad, RELATIVE cantidad, ALL, FORWARD [cantidad | ALL] o
BACKWARD [cantidad | ALL]. La omisión de esta cláusula implica que el gestor asuma por
defecto el valor NEXT para la misma.
NEXT: selecciona la fila siguiente.
PRIOR: selecciona la fila anterior.
FIRST: selecciona la primera fila de la consulta (es lo mismo que: ABSOLUTE 1).
LAST: selecciona la última fila de la consulta (es lo mismo que: ABSOLUTE -1).
ABSOLUTE cantidad: selecciona la n-ésima fila de la consulta. Si cantidad es negativo se
selecciona la n-ésima fila contando a partir de la última.
RELATIVE cantidad: selecciona la n-ésima fila que sigue a la fila actual. Si cantidad es negativo
se selecciona la n-ésima fila que precede a la fila actual. RELATIVE 0 selecciona la fila actual.
ALL: selecciona todas las filas siguientes (es lo mismo que FORDWARD ALL).
FORWARD [cantidad | ALL]: en el caso de cantidad, selecciona las n filas siguientes. En el
caso de ALL selecciona todas las filas siguientes. De lo contrario es igual que NEXT.
BACKWARD [cantidad | ALL]: en el caso de cantidad, selecciona las n filas anteriores. En el
caso de ALL selecciona todas las filas anteriores. De lo contrario es igual que PRIOR.
4. Cerrar el cursor: en este paso se libera la memoria que ocupa y se impide su
procesamiento. Para realizar esta operación se utiliza:
CLOSE nombre_cursor;
Tras cerrar un cursor este podría ser abierto de nuevo (Paso 2) y volvería a ejecutar la
consulta asociada.
DISPARADORES (TRIGGER)
Los disparadores o trigger son objetos que permiten ejecutar funciones de forma automática
dentro del gestor de base de datos, sin necesidad de que sea el usuario quien mande a ejecutar
una operación determinada. Su nivel de autonomía potencia las ventajas de las Funciones antes
mencionadas.
Un disparador es una acción en cadena que empieza antes o después de la ocurrencia de un
evento específico (INSERT, DELETE, UPDATE, TRUNCATE) sobre una tabla específica. Su uso
está muy vinculado con la realización de acciones para auditar y monitorear las actividades de
cambio de datos dentro de una base de datos.
Ventajas de los disparadores:
• Pueden consultar otras tablas e incluir sentencias SQL complejas.
• Son especialmente útiles para exigir reglas o requisitos complejos.
• Son útiles para exigir la integridad referencial, que conserva las relaciones definidas entre
tablas cuando se agregan, actualizan o eliminan filas en esas tablas.
• Son automáticos, se activan al efectuarse modificaciones en los datos de la tabla, como una
entrada manual o una acción de la aplicación.
• Pueden realizar cambios en cascada a través de tablas relacionadas de la base de datos, al
igual que cuando se define las cláusulas ON DELETE y ON UPDATE al definir restricciones
de integridad referencial al crear tablas.
• Pueden exigir restricciones más complejas que las definidas con restricciones CHECK.
• Ofrecen un mayor control sobre una Base de Datos.
Desventajas:
• Hay que definir con anticipación la tarea que realizará (función asociada).
• Peligro de pérdida en reorganizaciones al realizar reinserción de datos al modificar.
• No se pueden utilizar en tablas temporales.
1. Una función asociada, que se ejecuta en respuesta al o los eventos que se controlen.
La función asociada es similar a las estudiadas con anterioridad; pero tiene la característica de
que su tipo de dato de retorno siempre va a ser trigger. Además, dependiendo de las acciones del
disparador, el parámetro del RETURN dentro del cuerpo de la función puede variar y es obligatorio.
La sintaxis genérica de esta función sería entonces:
CREATE OR REPLACE FUNCTION nombre_función () RETURNS TRIGGER AS $$
[DECLARE variables]
BEGIN
…
RETURN párametro;
END;
$$
LANGUAGE 'plpgsql';
De ellas las más empleadas son NEW y OLD. Ambas, brindan la posibilidad de acceder a los datos
que se almacenan dentro de los eventos que se controlan, es por ello su empleo depende del
evento. En la siguiente tabla se observan los valores a almacenar según el evento:
Los ejemplos de cómo dotar a una BD de un comportamiento más activo, al emplear un disparador,
pueden ser consultados en el material Implementando Disparadores.
Antes de terminar, debes conocer que los objetos estudiados (Funciones y Disparadores) pueden
ser eliminados haciendo uso del comando DROP del lenguaje DDL. Por ejemplo:
ORIENTACIONES FINALES
• Al término de esta Lección usted está en condiciones de realizar la actividad práctica
Trabajando con Funciones y Disparadores correspondiente al Tema 2, disponible en la
plataforma
• Una vez concluida la actividad práctica realice el Cuestionario Evaluativo del Tema 2