Páginas

26 de agosto de 2015

PostgreSQL: Funciones, Procedimientos y Vistas

Este es el Post #9 del Topic: Guía Práctica de PostgreSQL

El diagrama siguiente es la consecuencia de la creación de la base de datos con el script descrito en PostgreSQL: Sentencias DDL (1), Es un modelo pequeño, pero servirá para las futuras demostraciones con postgres.

Descripción:
Una universidad realiza el registro de contactos, que son las personas que podrían ser potenciales postulantes a diferentes carreras. Los postulantes deberán pertenecer a un periodo académico y deberán elegir una modalidad asì como la carrera a la que quieren postular. Por lo general en un año solo existen dos periodos académicos, por ejemplo los periodos académicos del año 2008 fueron: 2008-1 y 2008-2. El contacto debe tener los datos personales del sujeto asì como la fecha de creación. El postulante debe tener registrado si asistió a su exámen de admisión, y si ingresó o no, debe tener también establecido el puntaje que alcanzó en el exámen.

Con esos datos podemos comenzar a elaborar nuestras primeras consultas usando instrucciones DML.

Modelo Relacional Base de datos
Antes de ir al grano, aclaro que acostumbro crear funciones cuando se trata de retornar datos, como por ejemplo una consulta que utilice la cláusula SELECT, y procedimientos almacenados únicamente cuando se trata de realizar una operación que no retorne ningún dato, por ejemplo, INSERT, UPDATE o DELETE.

En SQLServer se suele utilizar un Procedimiento Almacenado para cualquiera de los 2 casos descritos, aunque en PostGres también se puede retornar datos con procedimientos almacenados, sería ideal respetar la funcionalidad de cada uno. Las funciones desarrolladas en éste post se realizará haciedno uso del lenguaje procedural ‘plpgsql’, que generalmente trae  PostGreSQL.

Si aún no tienes preparado la base de datos, aqui les dejo un script. Es la unión de las DDL y DML creadas en posts anteriores. Ésta creará el entorno en el que se realizarán las demostraciones.
Descargar Script: DBAdmision.sql

FUNCIONES
:
1) Cree una función que devuelva los siguientes datos: <ver>
Periodo 101 309 310 Total
2005-1 1 7 7 15
2005-2 0 13 12 25
2006-1 0 17 18 35
2006-2 0 23 22 45
2007-1 0 27 28 55
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
CREATE OR REPLACE FUNCTION f_ejemplo_1()
RETURNS SETOF "record" AS
$BODY$
DECLARE
    r RECORD;
BEGIN
    FOR r IN
        SELECT idperacad AS "Periodo"
        ,SUM(CASE WHEN IDCarrera = '101' THEN 1 ELSE 0 END ) AS "101"
        ,SUM(CASE WHEN IDCarrera = '309' THEN 1 ELSE 0 END ) AS "309"
        ,SUM(CASE WHEN IDCarrera = '310' THEN 1 ELSE 0 END ) AS "310"
        ,COUNT(*) AS "TOTAL"
        FROM Persona.Postulante
        GROUP BY IDPerAcad
    LOOP
        RETURN NEXT r;
    END LOOP;
RETURN;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
 
--SELECT * FROM f_ejemplo_1() AS ("Periodo" CHARACTER, "101" bigint,"309" bigint,"310" bigint,"TOTAL" bigint);

2) Cree una función que devuelva los siguientes datos:
<ver>
Periodo 101 309 310 Total
2005-1 1 7 7 15
2005-2 0 13 12 25
2006-1 0 17 18 35
2006-2 0 23 22 45
2007-1 0 27 28 55
TOTAL 1 87 87 175
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
CREATE OR REPLACE FUNCTION f_ejemplo_2()
RETURNS SETOF "record" AS
$BODY$
DECLARE
    r RECORD;
BEGIN
    FOR r IN
        SELECT idperacad AS "Periodo"
        ,SUM(CASE WHEN IDCarrera = '101' THEN 1 ELSE 0 END ) AS "101"
        ,SUM(CASE WHEN IDCarrera = '309' THEN 1 ELSE 0 END ) AS "309"
        ,SUM(CASE WHEN IDCarrera = '310' THEN 1 ELSE 0 END ) AS "310"
        ,COUNT(*) AS "TOTAL"
        FROM Persona.Postulante
        GROUP BY IDPerAcad
        UNION
        SELECT 'TOTAL' AS "Periodo"
        ,SUM(CASE WHEN IDCarrera = '101' THEN 1 ELSE 0 END ) AS "101"
        ,SUM(CASE WHEN IDCarrera = '309' THEN 1 ELSE 0 END ) AS "309"
        ,SUM(CASE WHEN IDCarrera = '310' THEN 1 ELSE 0 END ) AS "310"
        ,COUNT(*) AS total
        FROM Persona.Postulante
    LOOP
        RETURN NEXT r;
    END LOOP;
RETURN;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
 
--SELECT * FROM f_ejemplo_2() AS ("Periodo" CHARACTER, "101" bigint,"309" bigint,"310" bigint,"TOTAL" bigint);

3) Cree una función que liste todos los postulantes de un determinado periodo académico. La lista debe reiniciar la numeración por orden alfabético.
<ver>
Apellidos y Nombres
1 Alanya Padilla Alina Susan
2 Alarcon Castro Gustavo Claudio Andres
1 Baldeón Balvín Olger
2 Baldeón Sanabria Natalia Ivonne
1 Cachuán Cámac Miguel Fernando
2 Cajachagua Chui Jose Arturo

4) Cree una función que liste todos los postulantes, la lista debe estar enumerada y ordenada por apellido paterno, materno y nombres.
<Requerimiento> <ver>
Apellidos y Nombres
1 Alanya Padilla Alina Susan
2 Alarcon Castro Gustavo Claudio Andres
3 Alarco Lama Ricardo Rafael
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CREATE OR REPLACE FUNCTION f_ejemplo_4()
RETURNS SETOF "record" AS
$BODY$
DECLARE
    r RECORD;
BEGIN
    FOR r IN
        SELECT rownumber() "Numero",* FROM
        (SELECT PC.Paterno || ' ' || PC.Materno || ' ' || PC.Nombres "Apellidos y Nombres"
        FROM Persona.Contacto PC
        INNER JOIN Persona.Postulante PP ON PP.IDContacto=PC.IDContacto
        ORDER BY PC.Paterno,PC.Materno,PC.Nombres) AS tb2
    LOOP
        RETURN NEXT r;
    END LOOP;
RETURN;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
 
--SELECT * FROM f_ejemplo_4() AS ("Numero" INTEGER, "Apellidos y Nombres" TEXT);
1
2
3
4
5
6
7
8
CREATE OR REPLACE FUNCTION rownumber() RETURNS integer AS $$
BEGIN
EXECUTE 'CREATE TEMP SEQUENCE "'||current_timestamp||'"';
RETURN nextval('"'||current_timestamp||'"');
EXCEPTION WHEN duplicate_table THEN RETURN nextval('"'||current_timestamp||'"');
END
$$ LANGUAGE 'plpgsql';
;;

5) Cree una función que reciba como parámetro el id de un contacto y que muestra la siguiente información. Se debe de mostrar todos los periodos académicos y una X en todos aquellos en la cual el contacto postuló.
<ver>
Periodo Postuló
2005-1 X
2005-2
2006-1
2006-2
2007-1 X

6) Cree una función que muestre un resumen como el siguiente:
<ver>

Apellidos y Nombres 2005-1 2005-2 2006-1 2006-2 2007-1 2007-2 TOTAL
Alanya Padilla Alina Susan



X
1
Alarcon Castro Gustavo Claudio Andres
X



1
Alarco Lama Ricardo Rafael

X


1
Altez Yanez Jasmin Gabriela
X



1
Amable Salva Katerin Lisbet



X
1








7) Cree una función que muestre un resumen como el siguiente:
<ver>

Puntaje 2005-1 2005-2 2006-1 2006-2 2007-1 2007-2 TOTAL
0 1 3 1 2 2
9
12

1


1
13

1


1
14



1
1
21
1

1
2
23
1
3

4









8.) Cree una función que liste todos aquellos postulantes (Apellidos, Nombres, Puntaje) cuyo puntaje es mayor o igual al puntaje promedio obtenido en todos los exámenes.
<ver>

VISTAS
:
9) Cree una vista que liste a todos aquellos postulantes que no asistieron al examen. Muestre los nombres, Apellidos, edad y género en formato (Varón/Mujer). <ver>

10) Cree una vista que liste a todos aquellos postulantes que ingresaron cuyo cumpleaños sea mañana (Entiéndase mañama como el dia después de la fecha actual). Muestre nombres y apellidos.
<ver>
11) Cree una vista que muestre un resumen como el siguiente: <ver>
Nombre 2005-1 2005-2 2006-1 2006-2 2007-1 TOTAL
Administración 5 8 12 14 23 62
Contabilidad 6 7 13 17 22 65
Ingeniería Informática 0 0 0 0 0 0
TOTAL 11 15 25 31 45 127
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
CREATE OR REPLACE VIEW v_ejemplo_11
AS
(
    SELECT AC.Nombre,SUM(CASE WHEN PP.Ingreso='1' AND PP.IDPerAcad IN ('2005-1') THEN 1 ELSE 0 END) "2005-1",
    SUM(CASE WHEN PP.Ingreso='1' AND PP.IDPerAcad IN ('2005-2') THEN 1 ELSE 0 END) "2005-2",
    SUM(CASE WHEN PP.Ingreso='1' AND PP.IDPerAcad IN ('2006-1') THEN 1 ELSE 0 END) "2006-1",
    SUM(CASE WHEN PP.Ingreso='1' AND PP.IDPerAcad IN ('2006-2') THEN 1 ELSE 0 END) "2006-2",
    SUM(CASE WHEN PP.Ingreso='1' AND PP.IDPerAcad IN ('2007-1') THEN 1 ELSE 0 END) "2007-1",
    SUM(CASE WHEN PP.Ingreso='1' AND PP.IDPerAcad IN ('2005-1','2005-2','2006-1','2006-2','2007-1') THEN 1 ELSE 0 END) "TOTAL"
    FROM Persona.Contacto PC
    INNER JOIN Persona.Postulante PP ON PP.IDContacto=PC.IDContacto
    INNER JOIN Admision.Carrera AC ON AC.IDCarrera=PP.IDCarrera
    GROUP BY AC.Nombre
 
    UNION
 
    SELECT 'TOTAL',SUM(CASE WHEN PP.Ingreso='1' AND PP.IDPerAcad IN ('2005-1') THEN 1 ELSE 0 END) "2005-1",
    SUM(CASE WHEN PP.Ingreso='1' AND PP.IDPerAcad IN ('2005-2') THEN 1 ELSE 0 END) "2005-2",
    SUM(CASE WHEN PP.Ingreso='1' AND PP.IDPerAcad IN ('2006-1') THEN 1 ELSE 0 END) "2006-1",
    SUM(CASE WHEN PP.Ingreso='1' AND PP.IDPerAcad IN ('2006-2') THEN 1 ELSE 0 END) "2006-2",
    SUM(CASE WHEN PP.Ingreso='1' AND PP.IDPerAcad IN ('2007-1') THEN 1 ELSE 0 END) "2007-1",
    SUM(CASE WHEN PP.Ingreso='1' AND PP.IDPerAcad IN ('2005-1','2005-2','2006-1','2006-2','2007-1') THEN 1 ELSE 0 END) "TOTAL"
    FROM Persona.Contacto PC
    INNER JOIN Persona.Postulante PP ON PP.IDContacto=PC.IDContacto
    INNER JOIN Admision.Carrera AC ON AC.IDCarrera=PP.IDCarrera
);
--SELECT * FROM v_ejemplo_11;
 
PROCEDIMIENTOS ALMACENADOS:
12) Cree un procedimiento almacenado que permita eliminar los postulantes registrados correspondientes a una modalidad. <ver>
1
2
3
4
5
6
7
8
9
CREATE OR REPLACE PROCEDURE sp_ejemplo_12(p_IDModalidad varchar)
AS
BEGIN
    DELETE FROM Persona.Postulante
    WHERE IDModalidad=p_IDModalidad;
 
END
--EXEC sp_ejemplo_12('2');
--SELECT * FROM Persona.Postulante;
13)Cree un procedimiento almacenado que permita registrar a un nuevo contacto. <ver>
14)Cree un procedimiento almacenado que permita actualizar los datos de un postulante. <ver>
TOPIC:
FUNCTION, PROCEDURE, VIEW
 
Fuente:
https://saforas.wordpress.com/2009/11/08/funciones-y-procedimientos-en-postgresql/