You are on page 1of 6

Tema 5 – PL/PGSQL

Extensiones SQL-
SQL-99

ƒ Entre las propuestas definidas en el estándar


SQL-99 están la extensión del SQL dotándolo
de comandos "procedurales":
ƒ Extensión "procedural" de SQL – If, where, loop, etc (control de flujo)
ƒ El standard SQL-99 esta bastante lejos de ser
satisfecho por la mayoría de las bases de
datos.
ƒ Oracle tiene una versión propia de estas
extensiones "procedurales" llamada PL/SQL
ƒ PostgreSQL tiene su versión llamada PL/pgSQL
2005/2006 2005/2006
PL/PGSQL 1 PL/PGSQL 2

PL/pgSQL
PL/pgSQL Un paso previo a usar PL/pgSQL
PL/pgSQL

ƒ Permite crear funciones que se ejecutan en el ƒ Cuando se crea una base de datos nueva hace falta
servidor (versus otras aproximaciones como "autorizar" el uso de pl/pgSQL (a menos que template1
ya este autorizada)
JDBC que se ejecutan en el cliente con
"overhead" de comunicaciones). createdb mydatab
ƒ La propia base de datos se encarga de createlang plpgsql mydatab
compilar y gestionar estas funciones con lo que ƒ En los laboratorios debería estar "autorizado" por
suelen ser eficientes defecto
ƒ proporciona:
– variables
– bucles ƒ Ojo: no se comprueba la sintaxis de las funciones hasta
que no son ejecutadas. (Es difícil depurar el código)
– evaluación condicional
2005/2006 2005/2006
PL/PGSQL 3 PL/PGSQL 4
PL/pgSQL
PL/pgSQL:: Estructura de las funciones PL/pgSQL
PL/pgSQL estructura
ƒ PL/pgSQL presenta una estructura en "Bloques".
CREATE FUNCTION nombre_función (argumentos)
ƒ Cada bloque se define usando RETURNS type AS '
DECLARE DECLARE
--variables inicializada con NULL cada vez declaracion; --variables
--que se entra en el bloque [...]
[...] BEGIN
BEGIN statement; --comandos
--comandos; [...]
[...] END;
END; ' LANGUAGE 'plpgsql';
ƒ No se pueden definir transacciones dentro de una ƒ Una función puede constar de varios bloques y estos
función pueden estar anidados

2005/2006 2005/2006
PL/PGSQL 5 PL/PGSQL 6

PL/pgSQL
PL/pgSQL:: Generalidades
Ejemplo trivial sin pasar parámetros
ƒ ¿Qué hace esta función?
ƒ Los tipos de datos pasados a la función se dan en CREATE OR REPLACE FUNCTION una_funcion () RETURNS
paréntesis int4 AS
ƒ El cuerpo de la función se pasa a la base de datos ' DECLARE
como una cadena de caracteres (véase, que el cuerpo an_integer int4; --variables
empieza y acaba con comillas simples) BEGIN
an_integer := 10 * 10; --comandos
ƒ Tras la cadena el lenguaje usado para crear la función
RETURN an_integer;
se define usando la orden "LANGUAJE" (otros
END;
lenguajes posibles son PL/PERL, PL/TCL, C, etc)
' LANGUAGE 'plpgsql';
____________________________
select una_funcion();
una_funcion
------------
100
(1 row)
2005/2006 2005/2006
PL/PGSQL 7 PL/PGSQL 8
Tipos de Variables-
Variables-I Tipos de Variables II

ƒ Ejemplos de variables: ƒ Todos los tipos de variable definidos para SQL son
válidos en PL/pgSQL
id_usuario INTEGER;
cantidad NUMERIC(5); ƒ No es imprescindible conocer el tipo de variables de los
url VARCHAR; atributos
– Ejemplos usando %TYPE
-- Mas sobre los tipos siguientes más adelante DECLARE … mivar tabla.salario%TYPE;
micampo mitabla.campo%TYPE; BEGIN … RETURN mivar*2;
mitupla mitabla%ROWTYPE; – %ROWTYPE reserva sitio para toda la tupla
– Reteniendo la estructura de los datos
ƒ The general syntax of a variable declaration is: DECLARE … mivar tabla%ROWTYPE;
BEGIN … RETURN mivar.salario*2;
name [ CONSTANT ] type [ NOT NULL ] [ { DEFAULT | :=
} expression ];

2005/2006 2005/2006
PL/PGSQL 9 PL/PGSQL 10

Más sobre Variables Ejemplo trivial pasando variables


CREATE OR REPLACE FUNCTION cal_longitud (text)
ƒ CREATE FUNCTION mifuncion(INTEGER, CHAR, …) RETURNS int4 AS
' DECLARE
ƒ Se pueden pasar hasta 16 variables
intext ALIAS FOR $1; --primer parametro
– $1, $2, …, $16
resultado int4;
ƒ ALIAS permite renombrar variables BEGIN
resultado := (SELECT LENGTH(intext));
CREATE FUNCTION cal_longitud (text) RETURNS RETURN resultado;
int4 AS END;
' DECLARE ' LANGUAGE 'plpgsql';

intext ALIAS FOR $1; --primer parametro ____________________________


SELECT cal_longitud('palabra');
resultado int4;
cal_longitud
. . . --------------
7
2005/2006 2005/2006
PL/PGSQL 11 (1 row) PL/PGSQL 12
Ejemplo usando Rowtype Ejemplo usando Rowtype
CREATE OR REPLACE FUNCTION trae_pelicula (integer)
RETURNS text AS
' DECLARE nueva=> select trae_pelicula(3);
pelicula_id ALIAS FOR $1;
encontrada_pelicula pelicula%ROWTYPE; trae_pelicula
BEGIN --------------------------------------------------------------------------------
SELECT INTO encontrada_pelicula * FROM pelicula Blade Runner (1982)
WHERE id = pelicula_id; (1 row)
RETURN encontrada_pelicula.titulo || '' ('' ||
encontrada_pelicula.agno || '')'';
END;
' LANGUAGE 'plpgsql';
ƒ Nota: Si SELECT INTO devuelve más de una tupla se
ignoran todas menos la primera (la solución a esto
más tarde)

2005/2006 2005/2006
PL/PGSQL 13 PL/PGSQL 14

Control de Flujo Ejemplo IF/ELSE


ƒ Programa que calcula la longitud de dos cadenas y
devuelve la longitud mayor.
CREATE OR REPLACE FUNCTION cadena_mas_larga(text,
ƒ PL/pgSQL contiene estructuras de control que permiten text) RETURNS int4 AS '
seleccionar las líneas de código que serán ejecutarse
in_uno ALIAS FOR $1;
en tiempo real. in_dos ALIAS FOR $2;
lon_uno int4;
ƒ IF…THEN…ELSE…ELSE IF lon_dos int4;
– ejecución condicional result int4;
BEGIN
ƒ LOOPS, WHILE LOOPS, FOR LOOPS lon_uno := (SELECT LENGTH(in_uno));
– iteraciones lon_dos := (SELECT LENGTH(in_dos));
IF lon_uno > lon_dos THEN RETURN lon_uno;
– bucles ELSE RETURN lon_dos;
END IF;
END;
'LANGUAGE 'plpgsql';
ƒ NOTA 1: se pueden hacer condiciones mas
complicadas usando OR y AND
ƒ NOTA 2: Como PL/pgSQL se agrupa en bloques no
hacen falta paréntesis en torno a IF
ƒ Por que falla esta función?
2005/2006 2005/2006
PL/PGSQL 15 PL/PGSQL 16
Ejemplo bucle WHILE
Ejemplo IF/ELSE ƒ Función que cuenta cuantas veces aparece un carácter en una
ƒ Programa que calcula la longitud de dos cadenas y cadena
devuelve la longitud mayor. CREATE FUNCTION cuentac(text,text) RETURNS INT4 AS '
CREATE OR REPLACE FUNCTION cadena_mas_larga(text, text) DECLARE
RETURNS int4 AS intext ALIAS FOR $1; inchar ALIAS FOR $2;
' DECLARE lon int4; resultado int4;
in_uno ALIAS FOR $1; i int4; tmp char;
in_dos ALIAS FOR $2; BEGIN
lon_uno int4; lon := length(intext); i :=1;
lon_dos int4; resultado:=0;
result int4; WHILE i<= lon LOOP
BEGIN tmp := substr(intext,i,1);
lon_uno := (SELECT LENGTH(in_uno));
lon_dos := (SELECT LENGTH(in_dos)); IF tmp = inchar THEN
IF lon_uno > lon_dos THEN RETURN lon_uno; resultado := resultado +1;
ELSE RETURN lon_dos; END IF;
END IF; i:=i+1;
END; nueva=> SELECT cadena_mas_larga ('hola','adios'); END LOOP;
'LANGUAGE 'plpgsql'; RETURN resultado;
cadena_mas_larga END
------------------ ' LANGUAGE 'plpgsql';
5 -- SELECT cuentac('qwertytq','q');
2005/2006 2005/2006
(1 row) PL/PGSQL 17 PL/PGSQL 18

Excepciones Excepciones: Ejemplo


ƒ Calcular la suma de los enteros de n a m, usando la formula (p+1)*p/2
CREATE OR REPLACE FUNCTION suma(int4, int4) RETURNS int4
ƒ RAISE se usa para imprimir mensajes y, en el caso de AS '
DECLARE
excepcion, abortar la transacción inicio ALIAS FOR $1; fin ALIAS FOR $2;
resultado int;
ƒ RAISE { NOTICE | EXCEPTION} BEGIN
ƒ RAISE NOTICE IF (inicio <1) THEN
RAISE EXCEPTION ''inicio debe ser mayor que 1'';
– RAISE NOTICE ' No hagas eso!' '; ELSE
– RAISE NOTICE ' 'El señor' ' || id || ' 'no está en casa' '; IF(inicio <= fin) THEN
resultado := (fin+1)*fin/2 -
– RAISE NOTICE ' 'el señor % no está en casa' ' , id; (inicio-1)*inicio/2;
ELSE
RAISE EXCEPTION ''El valor inicial % debe ser menor
que el final %'', inicio, fin;
END IF;
END IF;
RETURN resultado;
END
' LANGUAGE 'plpgsql';
--SELECT suma(1,3);
2005/2006 2005/2006
PL/PGSQL 19 PL/PGSQL 20
SELECT y Bucles SELECT y Bucles
ƒ Cuantas tuplas empiezan con una letra determinada
CREATE OR REPLACE FUNCTION trae_pelicula (integer)
CREATE OR REPLACE FUNCTION cuenta_letra (text) RETURNS
RETURNS text AS ' int4 AS '
DECLARE DECLARE
pelicula_id ALIAS FOR $1; caracter ALIAS FOR $1; temporal record;
tmp_caracter text; resultado int4;
encontrada_pelicula pelicula%ROWTYPE; BEGIN
BEGIN resultado:=0;
SELECT INTO encontrada_pelicula * FROM pelicula FOR temporal IN SELECT titulo FROM pelicula LOOP
tmp_caracter :=substr(temporal.titulo,1,1);
WHERE id = pelicula_id;
IF tmp_caracter = caracter THEN
RETURN encontrada_pelicula.titulo || '' ('' || resultado := resultado +1;
encontrada_pelicula.agno || '')''; END IF;
END; END LOOP;
RETURN resultado;
' LANGUAGE 'plpgsql'; END;
'LANGUAGE 'plpgsql';

--SELECT cuenta_letra('A');
2005/2006 2005/2006
PL/PGSQL 21 PL/PGSQL 22