Está en la página 1de 30

PL/PGSQLPL/PGSQL

PL/PGSQL PL/PGSQL Extensión "procedural" de SQL

Extensión "procedural" de SQL

PeroPero primeroprimero triggerstriggers

DROP TABLE producto cascade; CREATE TABLE producto( id int PRIMARY KEY, vendido timestamp);

DROP FUNCTION modificacion() cascade; CREATE FUNCTION modificacion() RETURNS TRIGGER AS ' BEGIN NEW.vendido := ''now''; RETURN NEW; END; 'LANGUAGE 'plpgsql';

DROP TRIGGER t_modificacion on producto; CREATE TRIGGER t_modificacion BEFORE INSERT ON producto FOR EACH ROW EXECUTE PROCEDURE modificacion();

INSERT into producto VALUES (1); SELECT * FROM producto;

6.2

Trigger Layout

INSERT UPDATE DELETE OR
INSERT
UPDATE
DELETE
OR
ON

ON

ON
ON
Trigger Layout INSERT UPDATE DELETE OR ON BEFORE CREATE TRIGGER < trigger_name > AFTER OLD NEW

BEFORE

Trigger Layout INSERT UPDATE DELETE OR ON BEFORE CREATE TRIGGER < trigger_name > AFTER OLD NEW

CREATE TRIGGER

< trigger_name >

OR ON BEFORE CREATE TRIGGER < trigger_name > AFTER OLD NEW FOR EACH ROW < table_name

AFTER

OLD NEW

FOR EACH ROW

< trigger_name > AFTER OLD NEW FOR EACH ROW < table_name > FOR EACH STATEMENT EXECUTE
< trigger_name > AFTER OLD NEW FOR EACH ROW < table_name > FOR EACH STATEMENT EXECUTE

< table_name >

FOR EACH STATEMENT

EXECUTE PROCEDURE

< function_name >

(
(

< arguments >

);
);
< table_name > FOR EACH STATEMENT EXECUTE PROCEDURE < function_name > ( < arguments > );
< table_name > FOR EACH STATEMENT EXECUTE PROCEDURE < function_name > ( < arguments > );
< table_name > FOR EACH STATEMENT EXECUTE PROCEDURE < function_name > ( < arguments > );

6.3

ExtensionesExtensiones SQL-99SQL-99

Entre las propuestas definidas en el estándar SQL-99 están la extensión del SQL dotándolo de comandos para controlar el flujo:

If, where, loop, etc

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

6.4

PorPor quéqué PL/pgSQLPL/pgSQL

Permite crear funciones que se ejecutan en el servidor (versus otras aproximaciones como JDBC que se ejecutan en el cliente con "overhead" de comunicaciones).

La propia base de datos se encarga de compilar y gestionar estas funciones con lo que suelen ser eficientes.

proporciona:

variables

bucles

evaluación condicional

6.5

UnUn pasopaso previoprevio aa usarusar PL/pgSQLPL/pgSQL

Cuando se crea una base de datos nueva hace falta "autorizar" el uso de pl/pgSQL (a menos que template1 ya este autorizada)

createdb mydatab createlang plpgsql mydatab

En los laboratorios debería estar "autorizado" por defecto

Ojo: no se comprueba la sintaxis de las funciones hasta que no son ejecutadas. (Es difícil depurar el código)

6.6

PL/pgSQL:PL/pgSQL: EstructuraEstructura dede laslas funcionesfunciones

PL/pgSQL presenta una estructura en "Bloques".

Cada bloque se define usando

DECLARE

--variables inicializada con NULL cada vez --que se entra en el bloque

] [

BEGIN

--comandos;

] [

END;

No se pueden definir transacciones dentro de una función

6.7

PL/pgSQLPL/pgSQL estructuraestructura

CREATE FUNCTION nombre_función (argumentos) RETURNS type AS '

DECLARE

declaracion;

] [

BEGIN

statement;

] [

END;

--variables

--comandos

' LANGUAGE 'plpgsql';

Una función puede constar de varios bloques y estos pueden estar anidados

6.8

PL/pgSQL:PL/pgSQL: GeneralidadesGeneralidades

Los tipos de datos pasados a la función se dan en paréntesis (sin nombre de variable antes de postgres 8)

El cuerpo de la función se pasa a la base de datos como una cadena de caracteres (nótese, que el cuerpo empieza y acaba con comillas simples) (escape doble)

Tras la cadena el lenguaje usado para crear la función se define usando la orden "LANGUAJE" (otros lenguajes posibles son PL/PERL, PL/TCL, C, etc)

6.9

EjemploEjemplo trivialtrivial sinsin pasarpasar parámetrosparámetros

¿Qué hace esta función?

CREATE OR REPLACE FUNCTION una_funcion () RETURNS int4 AS

' DECLARE

an_integer int4; BEGIN an_integer := 10 * 10; RETURN an_integer; END;

--variables

--comandos

' LANGUAGE 'plpgsql';

select una_funcion(); una_funcion

------------

100

(1 row)

6.10

TiposTipos dede Variables-IVariables-I

Ejemplos de variables:

id_usuario INTEGER;

cantidad NUMERIC(5,2);

url VARCHAR;

-- Mas sobre los tipos siguientes más adelante

micampo mitabla.campo%TYPE;

mitupla mitabla%ROWTYPE;

The general syntax of a variable declaration is:

name [ CONSTANT ] type [ NOT NULL ] [ { DEFAULT | := } expression ];

6.11

TiposTipos dede VariablesVariables IIII

Todos los tipos de variable definidos para SQL son válidos en PL/pgSQL

No es imprescindible conocer el tipo de variables de los atributos

Ejemplos usando %TYPE

DECLARE …

BEGIN

mivar payroll.salario%TYPE;

… RETURN mivar*2;

%ROWTYPE reserva sitio para toda la tupla

Reteniendo la estructura de los datos

DECLARE …

BEGIN …

mivar payroll%ROWTYPE;

RETURN mivar.salario*2;

6.12

EjemploEjemplo trivialtrivial pasandopasando variablesvariables

CREATE OR REPLACE FUNCTION cal_longitud (text) RETURNS int4 AS

' DECLARE intext ALIAS FOR $1; --primer parametro resultado int4; BEGIN resultado := (SELECT LENGTH(intext)); RETURN resultado; END;

' LANGUAGE 'plpgsql';

SELECT cal_longitud('qwerty'); cal_longitud

--------------

6

(1 row)

6.13

MásMás sobresobre VariablesVariables

CREATE FUNCTION mifuncion(INTEGER, CHAR, …)

Se pueden pasar hasta 16 variables

$1, $2, …, $16

ALIAS permite renombrar variables

CREATE FUNCTION cal_longitud (text) RETURNS int4 AS

' DECLARE

intext ALIAS FOR $1; --primer parametro

resultado int4;

.

.

.

6.14

EjemploEjemplo usandousando RowtypeRowtype

CREATE OR REPLACE FUNCTION trae_pelicula (integer) RETURNS text AS '

DECLARE

pelicula_id ALIAS FOR $1;

encontrada_pelicula pelicula%ROWTYPE;

BEGIN

SELECT INTO encontrada_pelicula * FROM pelicula WHERE id = pelicula_id;

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)

6.15

EjercicioEjercicio

En la base de datos de películas crear dos funciones que nos sirvan para llenar la tabla reparto, dando el nombre de la pelicula y el nombre del actor

CREATE TABLE PELICULA( ID INTEGER, TITULO CHAR(70), AGNO DECIMAL(4), PUNTUACION FLOAT, VOTOS INTEGER, PRIMARY KEY (ID));

--

CREATE TABLE ACTOR ( ID INTEGER, NOMBRE CHAR(35), PRIMARY KEY (ID));

--

CREATE TABLE REPARTO(

-- Identificador único -- Titulo de la película -- Año de estreno -- Puntuación media -- Numero de votos

-- Identificador Único -- Nombre del actor/actriz

PELICULA_ID INTEGER, -- referencia a la tabla PELICULA

-- referencia a la tabla ACTOR_ID -- Orden en el reparto -- La estrella es 1,

ACTOR_ID INTEGER, ORD INTEGER,

--

FOREIGN KEY (PELICULA_ID ) REFERENCES PELICULA(ID), FOREIGN KEY (ACTOR_ID) REFERENCES ACTOR(ID), PRIMARY KEY (PELICULA_ID, ACTOR_ID));

6.16

ControlControl dede FlujoFlujo

Los programas no suelen ejecutarse de principio a fin sin exceptuar ninguna línea de código. PL/pgSQL contiene estructuras de control que permiten seleccionar las líneas de código que serán ejecutarse en tiempo real.

IF…THEN…ELSE…ELSE IF

ejecución condicional

LOOPS, WHILE LOOPS, FOR LOOPS

iteraciones

bucles

6.17

EjemploEjemplo IF/ELSEIF/ELSE

Programa que calcula la longitud de dos cadenas y devuelve la longitud mayor.

CREATE OR REPLACE FUNCTION cadena_mas_larga(text, text) RETURNS int4 AS ' DECLARE in_uno ALIAS FOR $1; in_dos ALIAS FOR $2; lon_uno int4; lon_dos int4; result int4;

BEGIN lon_uno := (SELECT LENGTH(in_uno)); lon_dos := (SELECT LENGTH(in_dos));

IF

lon_uno > lon_dos THEN RETURN lon_uno;

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

6.18

EjemploEjemplo buclebucle WHILEWHILE (FOR)(FOR)

Función que cuenta cuantas veces aparece un carácter en una cadena

CREATE OR REPLACE FUNCTION cuentac(text,text) RETURNS INT4 AS ' DECLARE intext ALIAS FOR $1; inchar ALIAS FOR $2;

lon int4; resultado int4;

i int4; tmp char;

BEGIN lon := length(intext)+1;

resultado:=0;

i:=1;

WHILE i <= lon LOOP tmp := substr(intext,i,1); IF tmp = inchar THEN resultado := resultado +1; END IF;

i:=i+1;

END LOOP; RETURN resultado; END ' LANGUAGE 'plpgsql'; -- SELECT cuentac('qwertytq','q');

6.19

ExcepcionesExcepciones

RAISE se usa para imprimir mensajes y, en el caso de excepcion, abortar la transacción

RAISE { NOTICE | EXCEPTION}

RAISE NOTICE

RAISE NOTICE ' No hagas eso!' ';

RAISE NOTICE ' 'El señor' ' || id || ' 'no está en casa' ';

RAISE NOTICE ' 'el señor % no está en casa' ' , id;

6.20

Excepciones:Excepciones: EjemploEjemplo

Calcular la suma de los enteros de n a m (usar la formula (p+1)*p/2

CREATE OR REPLACE FUNCTION suma(int4, int4) RETURNS int4 AS ' DECLARE inicio ALIAS FOR $1; fin ALIAS FOR $2; resultado int; BEGIN

IF (inicio <1)

THEN

RAISE EXCEPTION ''inicio debe ser mayor que 1'';

ELSE IF(inicio <= fin) THEN resultado := (fin+1)*fin/2 -

(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); $$

6.21

SELECTSELECT yy BuclesBucles

CREATE OR REPLACE FUNCTION trae_pelicula (integer) RETURNS text AS '

DECLARE

pelicula_id ALIAS FOR $1;

encontrada_pelicula pelicula%ROWTYPE;

BEGIN

SELECT INTO encontrada_pelicula * FROM pelicula WHERE id = pelicula_id;

RETURN encontrada_pelicula.titulo || '' ('' || encontrada_pelicula.agno || '')'';

Nota: Si SELECT INTO devuelve más de una tupla se ignoran todas menos la primera (la solución a esto más tarde)

6.22

SELECTSELECT yy BuclesBucles

Cuantas tuplas empiezan con una letra determinada

CREATE OR REPLACE FUNCTION cuenta_letra (text) RETURNS int4 AS

'

DECLARE caracter ALIAS FOR $1; temporal record; tmp_caracter text; resultado int4; BEGIN

resultado:=0;

FOR temporal IN SELECT titulo FROM pelicula LOOP tmp_caracter :=substr(temporal.titulo,1,1); IF tmp_caracter = caracter THEN resultado := resultado +1; END IF; END LOOP; RETURN resultado; END; 'LANGUAGE 'plpgsql'; SELECT cuenta_letra('A');

6.23

Examen Parcial Nov

PUJA

Persona Puja
Persona
Puja

PERSONA

Examen Parcial Nov • PUJA Persona Puja PERSONA Puja Objeto OBJETO Subastado por • Rechazar pujas
Puja Objeto
Puja
Objeto
Parcial Nov • PUJA Persona Puja PERSONA Puja Objeto OBJETO Subastado por • Rechazar pujas no

OBJETO

Subastado por •
Subastado por

Rechazar pujas no admisibles

• 10% menos que puja anterior

• Un día más tarde

Actualizar campos redundantes

• vendido

6.24

CREATE TABLE persona( id SERIAL, --identificador unico PRIMARY KEY(id)

);

TABLASTABLAS

CREATE TABLE objeto( id SERIAL, persona_id INT,

--identificador unico

);

fecha_salida TIMESTAMP DEFAULT now(),

vendido int DEFAULT 0,

precio_salida NUMERIC(10,2), PRIMARY KEY(id), FOREIGN KEY (persona_id) REFERENCES persona(id)

-- 1 vendido, 0 no vendido

CREATE TABLE puja( objeto_id INT, persona_id INT, fecha TIMESTAMP, cuantia NUMERIC(10,2), FOREIGN KEY (objeto_id) REFERENCES objeto(id), FOREIGN KEY (persona_id) REFERENCES persona(id), PRIMARY KEY(persona_id,objeto_id,fecha)

);

6.25

--poblar la tabla

--

--persona (id)

--

INSERT INTO persona VALUES (1);-- INSERT INTO persona VALUES (2);-- INSERT INTO persona VALUES (3);-- INSERT INTO persona VALUES (4);-- INSERT INTO persona VALUES (5);--

DATOSDATOS

--

--objeto(id, persona_id,fecha_salida,vendido,precio_salida)

--

INSERT INTO objeto VALUES (1,1,now(),DEFAULT,23);-- INSERT INTO objeto VALUES (2,1,now()+'-1.1 day',DEFAULT,23); INSERT INTO objeto VALUES (3,3,now()+'-3.14 day',DEFAULT,23); INSERT INTO objeto VALUES (4,3,now()+'-2.9 day',DEFAULT,23); INSERT INTO objeto VALUES (5,3,now()+'-5.9 day',DEFAULT,23); INSERT INTO objeto VALUES (6,4,now()+'-5.9 day',DEFAULT,23); INSERT INTO objeto VALUES (7,4,now()+'-0.9 day',DEFAULT,23); INSERT INTO objeto VALUES (8,5,now()+'-0.8 day',DEFAULT,23); INSERT INTO objeto VALUES (9,5,now()+'-1.34 day',DEFAULT,23); INSERT INTO objeto VALUES (10,5,now()+'-5.9 day',DEFAULT,23);

--

--puja (objeto_id,persona_id,fecha)

--

INSERT INTO puja VALUES (1,1,now()+'1 hour',10.34);

INSERT INTO puja VALUES (1,1,now()+'2 hour',10.34);

INSERT INTO puja VALUES (1,1,now()+'3 hour',10.34);

INSERT INTO puja VALUES (1,1,now()+'4 hour',10.34);

INSERT INTO puja VALUES (1,1,now()+'5 hour',10.34);

INSERT INTO puja VALUES (1,1,now()+'10 hour',10.34);

INSERT INTO puja VALUES (10,1,now()+'1 hour',10.34);

INSERT INTO puja VALUES (9,1,now()+'1 hour',10.34);

6.26

EjemploEjemplo funciónfunción enen CC

Ejemplo Ejemplo función función en en C C

EjemploEjemplo dede FunciónFunción enen CC

Código

#include "postgres.h" #include <string.h>

/* by value */

int add_one(int arg)

{

return arg + 1;

}

gcc -fpic -c foo.c -I /usr/include/pgsql/server/

gcc -shared -o foo.so foo.o

Compilación

6.28

EjemploEjemplo dede FunciónFunción enen C-IIC-II

Instalacion:

como istrador de la base (postges):

CREATE FUNCTION add_one(integer) RETURNS integer

AS '/tmp/foo.so', 'add_one'

LANGUAGE C STRICT;

add_one.so es la librería dinámica con la función

Más información en:

Recordar que C no soporta todos los tipos usados en SQL (y viceversa)

6.29

SeSe acabóacabó

Se Se acabó acabó