Skip to content

Latest commit

 

History

History
741 lines (495 loc) · 23 KB

File metadata and controls

741 lines (495 loc) · 23 KB

Memoria Quevedo FEST

Introduccion

En este proyecto vamos a crear una pequeña base de datos para un hipotético festival.

Nos centraremos en el apartado de artistas musicales y sus actuaciones.

Modelo Conceptual

Para ello desarrollaremos una base de datos que incluya datos de los artista, sus grupos, sus fans, sus actuaciones, los asistentes a sus actuaciones asi como el lugar donde se realizan   y los patrocinadores de los grupos

Hemos decidido crear este esquema del modelo entidad relacion:


CAT


Modelo entidad-relacion

  Estos serian los datos que sacamos del anterior diagrama

  Grupo(id_grupo, nombre, experiencia)
  Artistas(nombre, apellido, dni)
  Estilo(epoca, nombre)
  Patrocinador(nombre, patrocinaDesde)
  Fans(id_fan, nombre, donacion)
  Actuacion(id_actuacion, nombre, fecha)
  Escenario(nombre, aforo)
  Asistente(numeroEntrada, nombre, butaca)

Normalizandolos,

  • Ya estan en primera forma normal, pues ningun atributo puede tomar mas de un valor por fila

  • Solo Patrocinador no esta en segunda forma normal

    • Aque al ser una relacion N-M lo que hacemos es crear una tabla intermedia nueva
  • Todos estan en tercera forma normal ya que ningun atributo depende de otro, solo de la clave primaria

    Grupo(id_grupo, nombre, experiencia)
    Artistas(nombre, apellido, dni)
    Estilo(epoca, nombre)
    Patrocinador(nombre, patrocinaDesde)
    Fans(id_fan, nombre, donacion)
    Actuacion(id_actuacion, nombre, fecha)
    Escenario(nombre, aforo)
    Asistente(numeroEntrada, nombre, butaca)
    
    Grupo-patrocinador(grupo, patrocinador, patrocinaDesde)
    

Ahora que hemos normalizado seguiremos las reglas de transformacion para generar las tablas

Modelo fisico

Obtendremos del apartado anterior los datos necesarios para crear este diagrama de clases.


CAT


Con esto, crearemos la base de datos “fest”.

Usando Postgres para crear la base de datos.

Expondremos aquí el codigo para crear la base de datos

CREATE DATABASE fest;

Tras crear la base de datos , creamos las tablas

CREATE TABLE estilo
(nombre VARCHAR(40) CONSTRAINT estilo_pk PRIMARY KEY,
epoca VARCHAR(40) NOT NULL
);

CREATE TABLE grupo
(id_grupo INT CONSTRAINT grupo_pk PRIMARY KEY,
nombre VARCHAR(40) UNIQUE NOT NULL,
experiencia DATE);

CREATE TABLE fan
(id_fan INT CONSTRAINT fan_pk PRIMARY KEY ,
nombre VARCHAR(40) NOT NULL,
donacion INT,
grupo INT,
CONSTRAINT fan_grupo_fk FOREIGN KEY (grupo) REFERENCES grupo(id_grupo)
);

CREATE TABLE artista
(dni VARCHAR(40) CONSTRAINT artista_pk PRIMARY KEY ,
nombre VARCHAR(40) NOT NULL,
apellido VARCHAR(40) NOT NULL,
grupo INT,
estilo VARCHAR(40),

CONSTRAINT artista_grupo_fk FOREIGN KEY (grupo) REFERENCES grupo(id_grupo),
CONSTRAINT artista_estilo_fk FOREIGN KEY (estilo) REFERENCES estilo(nombre)

);

CREATE TABLE escenario
(nombre VARCHAR CONSTRAINT escenario_pk PRIMARY KEY,
aforo INT
);

CREATE TABLE actuacion
(id_actuacion INT CONSTRAINT actuacion_pk PRIMARY KEY ,
nombre VARCHAR(40) NOT NULL,
fecha DATETIME NOT NULL,
grupo INT,
escenario VARCHAR(40),

CONSTRAINT actuacion_grupo_fk FOREIGN KEY (grupo) REFERENCES grupo(id_grupo),
CONSTRAINT escenario_estilo_fk FOREIGN KEY (escenario) REFERENCES escenario(nombre)

);

CREATE TABLE asistente
(num_entrada INT CONSTRAINT entrada_pk PRIMARY KEY ,
nombre VARCHAR(40) NOT NULL,
butaca VARCHAR(40),
actuacion INT,

CONSTRAINT asistente_grupo_fk FOREIGN KEY (actuacion) REFERENCES actuacion(id_actuacion)
);

CREATE TABLE patrocinador
(id_patrocinador VARCHAR(40) CONSTRAINT patrocinador_pk PRIMARY KEY,
nombre VARCHAR(40) NOT NULL
);

CREATE TABLE patrocinador_grupo
(
fecha_contrato DATE NOT NULL,
grupo INT CONSTRAINT patgrup_pk PRIMARY KEY,
patrocinador VARCHAR(40) UNIQUE NOT NULL,

CONSTRAINT patgrup_grupo_fk FOREIGN KEY (grupo) REFERENCES grupo(id_grupo),
CONSTRAINT patgrup_patrocinador_fk FOREIGN KEY (patrocinador) REFERENCES patrocinador(id_patrocinador)

);

Posteriormente introduciremos algunos datos ficticios en la base de datos

INSERT INTO grupo (id_grupo, nombre, experiencia)
VALUES (0, 'asd', '1990-10-12');
INSERT INTO grupo (id_grupo, nombre, experiencia)
VALUES (1, 'ACDC', '1945-12-12');

INSERT INTO estilo (nombre, epoca)
VALUES ('Barroco', 'Edad moderna');

INSERT INTO estilo (nombre, epoca)
VALUES ('Rock', 'Años 70');

INSERT INTO artista (dni, nombre, apellido, grupo, estilo)
VALUES('8374487G', 'Marco', 'Fernandez', 0, 'Barroco');

INSERT INTO artista (dni, nombre, apellido, grupo, estilo)
VALUES('1232387H', 'Marina', 'Ortiz', 1, 'Rock');

INSERT INTO patrocinador (id_patrocinador, nombre)
VALUES(0, 'Cocacola');

INSERT INTO patrocinador (id_patrocinador, nombre)
VALUES(1, 'Pepsi');

INSERT INTO patrocinador_grupo (fecha_contrato, grupo, patrocinador)
VALUES ('2020-10-1', 0, 0);

INSERT INTO patrocinador_grupo (fecha_contrato, grupo, patrocinador)
VALUES ('2018-11-11', 1, 1);

INSERT INTO fan (id_fan, nombre, donacion, grupo)
VALUES(0,'Carlos',10,0);

INSERT INTO fan (id_fan, nombre, donacion, grupo)
VALUES(1,'Jose',10,0);

INSERT INTO escenario (nombre, aforo)
VALUES('Principal', 1000);

INSERT INTO escenario (nombre, aforo)
VALUES('Secundario', 100);

INSERT INTO actuacion (id_actuacion, nombre, fecha, grupo, escenario)
VALUES(1,'Conciertillo','2021-11-2', 1, 'Secundario');

INSERT INTO actuacion (id_actuacion, nombre, fecha, grupo, escenario)
VALUES(0,'Conciertazo','2021-11-1', 0, 'Principal');

INSERT INTO asistente (num_entrada, nombre, butaca, actuacion)
VALUES(0, 'Miguel', '10G', 1);

INSERT INTO asistente (num_entrada, nombre, butaca, actuacion)
VALUES(1, 'Manuel', '4G', 0);

Despues de introducir esto datos de prueba vamos a modificar algunas cosas.

Primero, cambiaremos el nombre a la columna de EXPERIENCIA de la tabla GRUPO por FUNDACION, ya que es mas descriptiva.

ALTER TABLE grupo RENAME COLUMN experiencia TO fundacion;

Despues, cambiaremos tambien la columna Fechade la tabla ACTUACION que hemos definido como en un principio como tipo DATE y queremos que tambien incluya la hora de la actuacion.

Para esto primero borraremos la columna actual con

ALTER TABLE actuacion DROP COLUMN fecha;

Y luego añadiremos una columna nuevo de tipo TIMESTAMP a la misma columna.

ALTER TABLE actuacion ADD COLUMN fecha TIMESTAMP;

Por ultimo, reintroduciremos los datos de fecha en las entradas que ya estan en la base de datos.

UPDATE actuacion SET fecha = '2021-11-2 20:00:00' WHERE id_actuacion = 1;

UPDATE actuacion SET fecha = '2021-11-1 20:00:00' WHERE id_actuacion = 0;

Para no tener que crear una gran cantidad de datos a mano, hemos programado un pequeño programa en Python donde a partir de unos archivos sacados de la red creamos filas en las tablas.

import random
import datetime
import yaml

consultas = ''

ngrupos = 10 # max 30
nfans = 2000
max_artistas_grupo = 5
npatrocinadores = 50 # max 100
max_patrocinadores_por_grupo = 5
first_date_patrocinador = datetime.datetime(2016, 1, 1)
max_days_patrocinador = 1000
first_date_grupo = datetime.datetime(1980, 1, 1)
max_days_grupo = 40*356
escenarios = {'Principal': 2500, 'Secundario': 1000, 'Cubierto': 400}
# https://thestoryshack.com/tools/event-name-generator/
actuaciones = list(map(lambda x: x.strip(), open('actuaciones.dat', 'r').readlines()))
# https://thestoryshack.com/tools/band-name-generator/
grupos = list(map(lambda x: x.strip(), open('grupos.dat', 'r').readlines()))[:ngrupos]
# 10000 nombres mas frecuentes de hombre y 10000 mas frecuentes de mujer INE
nombres = list(map(lambda x: x.strip(), open('nombres.dat', 'r').readlines()))
# 10000 apellidos mas frecuentes INE
apellidos = list(map(lambda x: x.strip(), open('apellidos.dat', 'r').readlines()))
# https://www.duns100000.com/distribucion_empresas_ibericas/top100Espana
patrocinadores = list(map(lambda x: x.strip(), open('patrocinadores.dat', 'r').readlines()))[:npatrocinadores]

estilos = yaml.safe_load(open('estilos.yaml', 'r'))

for id_grupo, nombre in enumerate(grupos):
    fecha = (first_date_grupo + datetime.timedelta(days=random.randint(0, max_days_grupo))).strftime('%Y-%m-%d')
    consultas += f"INSERT INTO grupo (id_grupo,nombre,experiencia)\nVALUES({id_grupo},'{nombre}','{fecha}');\n"


for nombre, aforo in escenarios.items():
    consultas += f"INSERT INTO escenario (nombre, aforo)\nVALUES('{nombre}', {aforo});\n"

grupos_aleatorio = []
aforos_actuacion = []
while set(grupos_aleatorio) != set(range(ngrupos)):
    grupos_aleatorio = random.choices(range(ngrupos), k=len(actuaciones))

for id_actuacion, nombre in enumerate(actuaciones):
    escenario = random.choice(list(escenarios.keys()))
    aforos_actuacion.append(escenarios.get(escenario))
    fecha = f'2021-11-{random.randint(1,5)} {random.randint(0, 23)}:{random.choice(["00",15,30,45])}:00'
    consultas += f"INSERT INTO actuacion (id_actuacion,nombre,grupo,escenario,fecha)\nVALUES({id_actuacion},'{nombre}',{grupos_aleatorio[id_actuacion]},'{escenario}','{fecha}');\n"

num_entrada = 0
maxbutaca = 350
for actuacion, aforo in enumerate(aforos_actuacion):
    for i in range(aforo):
        consultas += f"INSERT INTO asistente (num_entrada,nombre,butaca,actuacion)\nVALUES({num_entrada},'{random.choice(nombres)}','{i%maxbutaca+1}{chr(65+i//maxbutaca)}',{actuacion});\n"
        num_entrada += 1


for id_patrocinador, nombre in enumerate(patrocinadores):
    consultas += f"INSERT INTO patrocinador (id_patrocinador,nombre)\nVALUES({id_patrocinador},'{nombre}');\n"

for grupo in range(ngrupos):
    nipatrocinadores = random.choices(range(len(patrocinadores)), k=random.randint(1, max_patrocinadores_por_grupo))
    for patrocinador in nipatrocinadores:
        fecha = (first_date_patrocinador + datetime.timedelta(days=random.randint(0, max_days_patrocinador))).strftime('%Y-%m-%d')
        consultas += f"INSERT INTO patrocinador_grupo (fecha_contrato,grupo,patrocinador)\nVALUES('{fecha}','{grupo}','{patrocinador}');\n"


for nombre, epoca in estilos.items():
    consultas += f"INSERT INTO estilo (nombre,epoca)\nVALUES('{nombre}','{epoca}');\n"


for id_fan in range(nfans):
    consultas += f"INSERT INTO fan (id_fan,nombre,donacion,grupo)\nVALUES({id_fan},'{random.choice(nombres)}',{random.randint(5, 500)},{random.randint(0, ngrupos-1)});\n"


for grupo in range(ngrupos):
    nartistas = random.randint(1, max_artistas_grupo)
    for _ in range(nartistas):
        rn = lambda: random.randint(0,9)
        dni = f'{rn()}{rn()}{rn()}{rn()}{rn()}{rn()}{rn()}{rn()}{chr(65+random.randint(0, 25))}'
        consultas += f"INSERT INTO artista (dni,nombre,apellido,grupo,estilo)\nVALUES('{dni}','{random.choice(nombres)}','{random.choice(apellidos)}',{grupo},'{random.choice(list(estilos.keys()))}');\n"


open('consulta.txt', 'w').write(consultas)

Gracias a este pequeño codigo generamos un txt donde estan todos los inserts de los datos.

Intentaremos conectar directamente con la base de datos si nos sobra tiempo para hacer directamente la insercion de datos.

Hemos introducido de esta forma 30.000 datos aproximadamente.

Consultas

Con estos datos ya podemos hacer consultas a la base de datos,

Dividiremos esta seccion en pequeñas consultas explicando su funcion en el proyecto y porque consideramos que serian utiles.

Con esta consulta podriamos ver a todos los asistentes a cualquier evento,

SELECT * FROM asistente;

Es una consulta sencilla que devolveria una gran cantidad de datos pero puede usarse para posteriormente filtrar los datos de otra forma, en vez de hacerlo directamente con la base de datos.

Con esta consulta veriamos los fans que han aportado mas de 100 euros a su grupo.

SELECT nombre FROM fan WHERE donacion > 100;

Tal vez podria ser util para organizar algun tipo de sorte entre los fans que mas han contribuido a la banda

Esta consulta ya es un poco mas compleja, ya que tiene una subconsulta. En ella bucamos el nombre de los grupos que tienen mas de un artista y que han recivido mas de 10000 euros en donaciones por sus fans

SELECT nombre FROM grupo AS gr WHERE (select count(*) from artista) > 1 AND (select sum(donacion) from fan where grupo = gr.id_grupo) > 10000;

Puede ser una consulta util para ver, entre los grupos de mas de un cantante, cuales ganan mas dinero de sus fans

Esta consulta tambien sencilla, veriamos cuantos asistentes tiene cada una de las actuaciones

SELECT actuacion, count(*) FROM asistente GROUP BY actuacion;

Puede ser util para comprobar que actuaciones son mas reclamadas y cuales menos

Con esta consulta comprobariamos que un grupo tenga una fundacion mas actual que el año 2000. Usamos DATE_PART para sacar el año de la fecha y lo comparamos.

SELECT * FROM grupo WHERE DATE_PART('year', fundacion) > 2000;

Util para buscar los grupos mas modernos.

Aqui ya hariamos consultas multitabla. En este caso consultariamos las tablas grupo y actuacion para relacionar el nombre de un grupo con la fecha en la que actua.

SELECT grupo.nombre, actuacion.fecha FROM grupo JOIN actuacion ON grupo.id_grupo=actuacion.grupo;

Seria interesante para sacar un calendario de fechas

En esta otra consulta multitabla relacionamos el nombre de un grupo con el total de donaciones de sus fans.

SELECT grupo.nombre, sum(fan.donacion) FROM grupo JOIN fan ON grupo.id_grupo=fan.grupo GROUP BY grupo.nombre;

Similar a una consulta anterior serviria para sacar los grupos con mas ingresos

Con esta consulta de agrupacion, veremos que estilos son mas o menos populares.

SELECT estilo, count(*) from artista GROUP BY estilo HAVING count(*) = 5;

Puede ser util para ver la tendencia musical actual. Aun que tal vez seria mas util de esta forma,

SELECT estilo, count(*) from artista GROUP BY estilo ORDER BY count(*);

Es consulta, relaciona el nombre de un patrocinador de un grupo con el nombre del escenario donde tocaria ese grupo en cada una de sus actuaciones. Seria una consulta multitabla con varios JOIN.

SELECT patrocinador.nombre, escenario.nombre FROM patrocinador 
JOIN patrocinador_grupo  ON patrocinador.id_patrocinador = patrocinador_grupo.patrocinador 
JOIN grupo ON patrocinador_grupo.grupo = grupo.id_grupo
JOIN actuacion ON grupo.id_grupo = actuacion.grupo
JOIN escenario ON actuacion.escenario = escenario.nombre;

Podria usarse para poder poner publicidad de ese patrocinador en el escenario en el que toque su grupo.

Tambien una consulta util seria la lista de estilos por fans.

SELECT estilo.nombre, count(fan.*) FROM fan 
JOIN grupo ON fan.grupo = grupo.id_grupo 
JOIN artista ON grupo.id_grupo = artista.grupo 
JOIN estilo ON artista.estilo = estilo.nombre 
GROUP BY estilo.nombre ORDER BY count(fan.*);

Por ultimo, podriamos relacionar la epoca de cada actuacion y su escenario para poder decorar el escenario acorde a esa actuacion.

SELECT escenario.nombre, estilo.epoca, actuacion.fecha FROM escenario 
JOIN actuacion ON escenario.nombre = actuacion.escenario 
JOIN grupo ON actuacion.grupo = grupo.id_grupo 
JOIN artista ON grupo.id_grupo = artista.grupo 
JOIN estilo ON artista.estilo = estilo.nombre;

Vistas

Las vistas son utiles usadas para consultas frecuentes, ya que nos ahorran tiempo al escribir estas consultas.

Debido a esto aqui proponemos algunas vistas que consideramos utiles

Esta primera vista nos relacionaria el nombre de cada grupo con sus artistas, una busqueda bastante util en festivales donde no se conozca mucho a los artistas.

CREATE or replace VIEW artistas AS 
   SELECT grupo.nombre, artista.nombre FROM grupo 
   JOIN artista ON grupo.id_grupo = artista.grupo ;

Creamos tambien una vista que nos muestra cada grupo con las fecha en la que actuan.

CREATE or replace VIEW calendarioGrupo AS 
   SELECT grupo.nombre, actuacion.fecha FROM grupo 
   JOIN actuacion ON grupo.id_grupo=actuacion.grupo ORDER BY grupo.nombre;

Y una ultima vista, bastante util para hacer un calendario junto a la anterior, donde ponemos cuanto conciertos haya cada dia.

CREATE or replace VIEW calendarioFechas AS 
   SELECT DATE_PART('day', actuacion.fecha) || '-' || DATE_PART('month', actuacion.fecha) || '-' || DATE_PART('year', actuacion.fecha), count(grupo.*) FROM grupo 
   JOIN actuacion ON grupo.id_grupo=actuacion.grupo 
   GROUP BY DATE_PART('day', actuacion.fecha), DATE_PART('month', actuacion.fecha), DATE_PART('year', actuacion.fecha)
   ORDER BY DATE_PART('day', actuacion.fecha);

Secuncias

Dado que el principal uso de secuencias es la introduccion de datos y nosotros usamos un archivo externo para la creacion de estos, no vemos mucha utilidad en crear secuencias.

Aun asi, a modo de ejemplo creamos una,

CREATE SEQUENCE secuencia INCREMENT 1 START 0;

Esta secuencia iria aumentando de 1 en 1 desde 0.

Indices

Los indices son utiles para consultas habituales sobre tablas con muchos datos. Aun que no es conveninte usarlos sobre tablas que tengan muchos cambios en sus datos habitualmente.

Hemos decidido hacer un indice para las siguientes columnas de las siguientes tablas

numEntrada de asistente

nombre de fan

Hemos elegido estas dos tablas y estas dos columnas porque ambas son tablas con muchos datos y columnas que se consultan bastante. Dado que nuestros datos son estaticos, no se van a añadir mas, no tendriamos problema con eso.

En caso de un escenario real habria que ver si realmente compensa, ya que son tablas propensas a muchos cambios habituales, especialemente la de los asistentes.

CREATE INDEX numEntrada ON asistente;

CREATE INDEX nombre ON fan;

Scripts

hemos generado tambien algunos scripts para hacer algunas funciones.

El primero es un script que sacaria por consola una lista de cada uno de los donantes.

Es muy sencillo por eso es el primero.

CREATE or replace FUNCTION donaciones(_grupo integer)
   RETURN text AS $$
declare 
    donado int;

	_donacion CURSOR(_grupo integer) 
		 for SELECT donacion
		 FROM fan
         WHERE grupo = _grupo;
		 
    donacionText text;

BEGIN

OPEN _donacion(_grupo);

donacionText := 'Estas han sido las donaciones de fans para el grupo con id %: ', _grupo;
   
   LOOP

    FETCH _donacion INTO donado;

    exit when not found;

    donacionText := donacionText || donado ;


   end loop;
  

   CLOSE _donacion;

   RETURN donacionText;

END;

 $$

language plpgsql;

Este segundo Script comprobaria si se ha superado el aforo maximo en algun evento para un escenario dado.

Para ello recorre cada uno de los eventos asociados a ese escenario y lo commpara con la suma de los asistentes.

En caso de superarse, lanzaria un mensaje informando del problema. Si no, lanzaria otro mensaje confirmando que aun quedan plazas.

CREATE or replace FUNCTION aforo_maximo(_escenario VARCHAR)
   RETURNS VOID
   
   AS $$

declare 
    v_actuacion RECORD;
    v_aforo escenario.aforo%type;
  
	_actuaciones CURSOR(_escenario VARCHAR)
		for SELECT actuacion.id_actuacion AS id_ac, count(asistente.*) AS Asistentes
		   FROM actuacion
         JOIN asistente ON actuacion.id_actuacion = asistente.actuacion
         GROUP BY actuacion.id_actuacion
         HAVING actuacion.escenario LIKE _escenario;
          
BEGIN

OPEN _actuaciones(_escenario);

SELECT aforo INTO v_aforo FROM escenario WHERE nombre = _escenario;


   LOOP

      FETCH _actuaciones INTO v_actuacion;

      exit when not found;

      IF (v_actuacion.Asistentes > v_aforo) then
         
         RAISE NOTICE 'Para la sesion % el aforo ha sido superado', v_actuacion.id_ac;
      
      ELSE RAISE NOTICE 'Para la sesion % el aforo aun no ha sido superado', v_actuacion.id_ac;
   
      END IF;

   END LOOP;
  
   CLOSE _actuaciones 

END;

 $$

language plpgsql;

Crearemos tambien un Script que devuelva true o false dependiendo de si el aforo de una actuacion esta lleno o no.

CREATE or replace FUNCTION aforo_lleno(_actuacion INTEGER)
   RETURNS BOOLEAN
   
   AS $$

declare 
    v_asistentes INTEGER;
    v_aforo escenario.aforo%type;
    _escenario escenario.nombre%type;
          
BEGIN

SELECT escenario.nombre INTO _escenario FROM escenario JOIN actuacion ON escenario.nombre = actuacion.escenario WHERE actuacion.id_actuacion = _actuacion;

SELECT aforo INTO v_aforo FROM escenario WHERE nombre = _escenario;

SELECT count(asistente.*) INTO v_asistentes
		FROM actuacion
         JOIN asistente ON actuacion.id_actuacion = asistente.actuacion
         WHERE actuacion.escenario LIKE _escenario AND actuacion.id_actuacion = _actuacion;

      IF (v_asistentes >= v_aforo) then
         
         RETURN true;
      
      ELSE 
      
        RETURN false;
   
      END IF;

END;

 $$

language plpgsql;

Hemos creado tambien un Script para hacer un sorteo entre los fans de un grupo que hayan aportado mas que cierta cantidad de donaciones.

El premio sera que recibira todas las donaciones de los concursantes y la suya propia de vuelta, por lo que considerares que todas esas donaciones son 0 para el grupo.

CREATE or replace FUNCTION Sorteo(_grupo integer, _dinero integer)
   RETURNS VARCHAR
   
   AS $$

declare 
    v_distancia integer;
    v_distancia_anterior integer;
    ganador VARCHAR;
    num_participantes integer;
    numGanador integer;
    num integer;
   
  
	_participantes CURSOR(_dinero integer, _grupo integer)
		for SELECT nombre from fan where grupo = _grupo AND donacion > _dinero; 
        
        --Al haber creado un indice previamente aqui lo estariamos usando
          

BEGIN

   SELECT count(*) INTO num_participantes from fan where grupo = _grupo AND donacion > _dinero;
   
   v_distancia_anterior := num_participantes;

   SELECT FLOOR(RANDOM()*num_participantes) INTO numGanador;

   FOR concursante IN _participantes(_dinero, _grupo) LOOP

      SELECT FLOOR(RANDOM()*num_participantes) INTO num;

      v_distancia := abs(num - numGanador);

      IF (v_distancia < v_distancia_anterior) THEN ganador := concursante; END IF;

      v_distancia_anterior := v_distancia;

      UPDATE fan
        SET donacion = 0
        WHERE CURRENT OF _participantes;

   END LOOP;

   RETURN ganador;


END;

 $$

language plpgsql;

Triggers

Crearemos tambien un trigger para cuando se introduzca un nuevo asistente a un evento.

Este trigger llamara a una funcion que llamara a un Script que hicimos previamente.

Su funcion sera comprobar si se ha superado el aforo, en caso de que si eliminara al nuevo asitente de la base de datos.

Primero creariamos una funcion para el trigger,

CREATE or replace FUNCTION trigger_nuevo_asistente() 

RETURNS TRIGGER 

LANGUAGE plpgsql 

AS
 $$
 DECLARE

   lleno BOOLEAN;

 BEGIN

 SELECT aforo_lleno(NEW.actuacion) INTO lleno;

 IF (lleno) THEN
    DELETE FROM asistente WHERE numEntrada = NEW.numEntrada;
 END IF;
    RETURN NEW;
 END;
 
 $$;

Posteriormente el propio trigger que llamaria a la funcion previamente creada,

CREATE TRIGGER trigger_asistente AFTER INSERT ON asistente 

EXECUTE FUNCTION trigger_nuevo_asistente();

Conclusiones

Con esto concluimos el proyecto de esta base de datos.

Hemos generado una base de datos suficiente para un festival, tanto con suficientes consultas sencillas para obtener datos importantes como funciones para consultas mas complejas para datos o acciones mas especificas.