Plpgsql - eureka.com.ve

Anuncio
PL/PGSQL
Extensión "procedural" de SQL
Pero primero triggers
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
BEFORE
CREATE TRIGGER
<trigger_name>
UPDATE
AFTER
DELETE
OLD NEW
OR
FOR EACH ROW
<table_name>
ON
FOR EACH STATEMENT
EXECUTE PROCEDURE
<function_name>
6.3
(
<arguments>
);
Extensiones SQL-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
Por qué PL/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
Un paso previo a usar PL/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: Estructura de las funciones
■ 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/pgSQL estructura
CREATE FUNCTION nombre_función (argumentos)
RETURNS type AS '
DECLARE
declaracion;
--variables
[...]
BEGIN
statement;
--comandos
[...]
END;
' LANGUAGE 'plpgsql';
■ Una función puede constar de varios bloques y estos pueden
estar anidados
6.8
PL/pgSQL: Generalidades
■ 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
Ejemplo trivial sin pasar parámetros
■ ¿Qué hace esta función?
CREATE OR REPLACE FUNCTION una_funcion () RETURNS
int4 AS
' DECLARE
an_integer int4;
--variables
BEGIN
an_integer := 10 * 10;
--comandos
RETURN an_integer;
END;
' LANGUAGE 'plpgsql';
____________________________
select una_funcion();
una_funcion
-----------100
(1 row)
6.10
Tipos de Variables-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
Tipos de Variables II
■ 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 …
mivar payroll%ROWTYPE;
BEGIN … RETURN mivar.salario*2;
6.12
Ejemplo trivial pasando variables
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ás sobre Variables
■ 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
Ejemplo usando Rowtype
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
Ejercicio
■ 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,
-- Identificador único
TITULO CHAR(70),
-- Titulo de la película
AGNO DECIMAL(4),
-- Año de estreno
PUNTUACION FLOAT,
-- Puntuación media
VOTOS INTEGER,
-- Numero de votos
PRIMARY KEY (ID));
-CREATE TABLE ACTOR (
ID INTEGER,
-- Identificador Único
NOMBRE CHAR(35),
-- Nombre del actor/actriz
PRIMARY KEY (ID));
-CREATE TABLE REPARTO(
PELICULA_ID INTEGER, -- referencia a la tabla PELICULA
ACTOR_ID INTEGER,
-- referencia a la tabla ACTOR_ID
ORD INTEGER,
-- Orden en el reparto
-- La estrella es 1, ...
-FOREIGN KEY (PELICULA_ID ) REFERENCES PELICULA(ID),
FOREIGN KEY (ACTOR_ID) REFERENCES ACTOR(ID),
PRIMARY KEY (PELICULA_ID, ACTOR_ID));
6.16
Control de Flujo
■ 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
Ejemplo IF/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
Ejemplo bucle WHILE
(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;
i:=1;
resultado:=0;
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
Excepciones
■ 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: Ejemplo
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
SELECT y Bucles
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
SELECT y Bucles
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
Puja
Objeto
PERSONA
OBJETO
Subastado por
•
Rechazar pujas no admisibles
•
10% menos que puja anterior
•
Un día más tarde
•
Actualizar campos redundantes
•
6.24
vendido
TABLAS
CREATE TABLE persona(
id SERIAL, --identificador unico
PRIMARY KEY(id)
);
CREATE TABLE objeto(
id SERIAL,
--identificador unico
persona_id INT,
fecha_salida TIMESTAMP DEFAULT now(),
vendido int DEFAULT 0,
-- 1 vendido, 0 no vendido
precio_salida NUMERIC(10,2),
PRIMARY KEY(id),
FOREIGN KEY (persona_id) REFERENCES persona(id)
);
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
DATOS
--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);----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
Ejemplo función en C
Ejemplo de Función en C
■ Código
#include "postgres.h"
#include <string.h>
/* by value */
int add_one(int arg)
{
return arg + 1;
}
■ Compilación
gcc -fpic -c foo.c -I /usr/include/pgsql/server/
gcc -shared -o foo.so foo.o
6.28
Ejemplo de Función en C-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:
http://developer.postgresql.org/docs/postgres/xfunc-c.
■ Recordar que C no soporta todos los tipos usados
en SQL (y viceversa)
6.29
Se acabó
Descargar