Examen Sistemas Informáticos I, Septiembre 2004

Anuncio
Examen Sistemas Informáticos I, Septiembre 2004
Se desea diseñar un sistema de reserva de habitaciones para la universidad. El sistema
tiene que ser capaz de:
Proporcionar un listado con las habitaciones reservadas
Buscar reservas clasificándolas por
• Fecha
• Habitación
• Persona que ha realizado la reserva
Debe ser posible borrar registros y la base de datos debe mantenerse integra, esto es,
no puede existir una habitación reservada a nombre de una persona cuyos datos no
estén almacenados en la tabla correspondiente. (Esta restricción debe ser reforzada
por la base de datos no por el interfaz de usuario)
Los profesores y personal administrativo deben ser capaces de reservar cualquier tipo
de habitación. Los estudiantes sólo habitaciones de uso general (salones de actos).
(Esta restricción debe ser reforzada por la base de datos no por el interfaz de
usuario)
Una habitación no puede estar reservada por dos personas simultáneamente. (Esta
restricción debe ser reforzada por la base de datos no por el interfaz de usuario)
Los usuarios se identificarán con una clave.
(3 puntos) Diseña RAZONADAMENTE una base de datos (diagrama entidad-relación)
que satisfaga los requerimientos anteriores, así como efectuar consultas como las de las
preguntas siguientes. Se deberán indicar las restricciones de integridad que contenga, y la
forma en que se especificarán en SQL tanto la estructura de las tablas como las restricciones
de integridad. En caso de que algún atributo pueda derivarse a partir de otros adjuntar la
consulta (en SQL) que proporcione el atributo redundante a partir del resto de los atributos
básicos. En la medida de lo posible, la base de datos y no el interfaz de usuario se debe encargar de todas las restricciones de integridad (mediante CHECKs, TRIGGERs, etc). Por
favor, ser breves en vuestros razonamientos pero es imprescindible que los hagais, un examen que contenga solo el diagrama E-R y las tablas será puntuado con zero.
(3 puntos) Dar RAZONADAMENTE consultas en el Álgebra relacional y en SQL
que permitan realizar las siguientes operaciones (en SQL se valorará la eficiencia de la
implementación):
a) Supón que el diseño de la base de datos no es óptimo y que tras borrar un usuario
no se borran todas las reservas realizadas por el mismo. Escribe la/s consulta/s que dado un usuario con nombre=’Pedro Pérez’ elimine al usuario de la base de datos y todas
las reservas que haya realizado. En caso de que sea necesario escribir varias consultas,
1
deseamos que esta operación sea atómica, esto es, se debe realizar o no realizar en su totalidad. El código SQL que escribas debe asegurar la atomicidad del proceso mediante el uso
de transacciones.
b) Para el miércoles 14 de Julio del 2004 de 13:30 a 15:30 Listar las habitaciones donde
se puedan alojar más de 200 personas. El listado constará del identificador de habitación
y de la palabra ’libre’ si la habitación está libre o la dirección de correo electrónico de la
persona que la haya reservado. El listado debe estar ordenado de forma que primero salgan
las habitaciones libres y luego las ocupadas
(3 puntos) Escribir RAZONADAMENTE un programa en C que permita hacer reservas. En particular el programa debe pedir al usuario que facilite los siguientes datos (Se
valorará la eficiencia de la implementación):
tipo de habitación
fecha
hora de inicio
hora de finalización
numero de participantes
persona que realiza la reserva y clave
a continuación proporcionará un listado como el del apartado b del punto anterior y permitirá al usuario que realice la reserva del aula que estime adecuada.
Recuerda que los estudiantes sólo pueden reservar salones de actos, la base de datos
dará un error si se intenta violar esta restricción. El programa debe procesar el error adecuadamente e informar al usuario del problema.
(1 punto) Comenta BREVE y RAZONADAMENTE que harías para que se enviara
automaticamente un mail a la persona que ha realizado la reserva si esta es cancelada. No es
necesario que presentes la implementación, sencillamente comenta como se podría hacer.
CALIFICACIONES: Se publicarán antes del 13 de septiembre
REVISIÓN DE EXÁMENES: 15 de septiembre a las 10:00 en el salón de grados
NOTA: No se revisarán exámenes a estudiantes que se presenten después de la hora de
la revisión.
2
Diseño de base de datos para realizar reservas
La base se puede implementar usando dos entidades: habitacion y persona y una relación
muchos a muchos reserva. Este diagrama da lugar a tres tablas.
-- tabla para la entidad habitación.
-- Utiliza un comando check para restringir
-- el tipo de habitacion
CREATE TABLE habitacion (
habitacion_ID int,
habitacion_tipo varchar(128)
CHECK(
habitacion_tipo IN(
’Clase’,
’Salon de Actos’,
’Sala de Juntas’,
’Salon de Grados’
)
) NOT NULL,
capacidad int, --numero máximo de personas
PRIMARY KEY (habitacion_ID)
);
-- tabla para la entidad persona
CREATE TABLE persona (
persona_ID int, --podria usarse el DNI, entonces tendría que ser char
nombre varchar(128),
username varchar(128),--nombre de usuario
-- username es redundante con persona_ID
-- pero seguramente sera conveniente
-- en una aplicacion real
clave varchar(128),--password
Es_Alumno boolean DEFAULT ’t’ NOT NULL,
Telefono char(12),
email varchar(128),
PRIMARY KEY (persona_ID),
UNIQUE (username)
);
-- función que comprueba si la habitación está disponible
-- la utilizará la tabla Reserva mediante un “Check”
-- Si la nueva reserva entra en conflicto con las reservas ya
-- almacenadas devuelve FALSE, en caso contrario TRUE
-- Se utiliza para implementar la restricción “Una habitación no
-- puede estar reservada por dos personas simultáneamente”
CREATE OR REPLACE FUNCTION disponible(int,timestamp, timestamp) RETURNS bool AS ’
DECLARE
myhabitacion_id ALIAS FOR $1;
mycomienzo ALIAS FOR $2;
myfin
ALIAS FOR $3;
BEGIN
IF(EXISTS(
SELECT habitacion_ID
FROM reserva
WHERE habitacion_ID=myhabitacion_id AND (
(mycomienzo between comienzo_reserva AND fin_reserva) OR
(myfin between comienzo_reserva AND fin_reserva)
3
)
)
)
THEN
RETURN FALSE;
END IF;
RETURN TRUE;
END;
’ LANGUAGE ’plpgsql’ VOLATILE;
-- tabla que implementa la relacion reserva
-- se hace uso de la funcion “disponible” para asegurarse de que no
-- hay conflictos de horario al hacer la reserva
-- la linea:
-- FOREIGN KEY (persona_ID) REFERENCES persona (persona_ID) ON DELETE CASCADE
-- asegura la integridad de la base, si se borra una persona,
-- se borran todas las reservas que ha realizado
CREATE TABLE Reserva (
persona_ID int,
-- persona que realiza la reserva
fecha_reserva date DEFAULT current_date, -- fecha en la que se realiza la reserva
comienzo_reserva timestamp, -- reserva empieza
fin_reserva timestamp,
-- reserva termina
habitacion_ID int ,
-- habitación reservada
number_of_participants int, -- No. participante estimado
FOREIGN KEY (persona_ID) REFERENCES persona (persona_ID) ON DELETE CASCADE,
FOREIGN KEY (habitacion_ID) REFERENCES habitacion (habitacion_ID) ON DELETE CASCADE,
CONSTRAINT habitacion_disponile CHECK (disponible(habitacion_ID,
comienzo_reserva,
fin_reserva))
);
-- trigger que comprueba que la reserva la ha hecho
-- una persona autorizada
-- Este codigo se ha implementado de forma que se pueda ejecutar
-- en postgres, lo cual es un poco mas restrictivo que el SQL 99
CREATE OR REPLACE FUNCTION autorizado_fun() RETURNS trigger AS ’
BEGIN
IF ( EXISTS (SELECT persona.persona_ID
FROM persona,habitacion,reserva
WHERE NEW.persona_ID=persona.persona_ID AND
Es_Alumno=”t” AND
habitacion_tipo NOT IN (”Salon de Actos”)
)
)
THEN RAISE EXCEPTION ”Usuario no autorizado”;
END IF;
RETURN NEW;
END;
’ LANGUAGE plpgsql;
DROP TRIGGER autorizado ON member;
CREATE TRIGGER init_alta BEFORE INSERT ON Reserva
FOR EACH ROW EXECUTE PROCEDURE autorizado_fun();
4
Consultas
a)
SQL
--Es necesario:
-- a) Encontrar el valor del atributo persona_ID para Pedro Perez
-- b) Comenzar una transaccion
-- c) Borrar todas las reservas hechas por Pedro Perez
-- d) Borrar a Pedro Perez de la tabla de Personas
-- e) terminar la transacción
-- en la base de datos de lo contrario el enunciado
-- está incompleto
BEGIN; -- or BEGIN TRANSACTION
CREATE VIEW USER_ID_Pedro_Perez AS
SELECT persona_ID
FROM persona
WHERE nombre=’Pedro Perez’;
DELETE from reserva
WHERE persona_ID IN (SELECT persona_ID
FROM persona
WHERE nombre=’Pedro_Perez’);
DELETE from persona
WHERE persona_ID IN (SELECT persona_ID
FROM persona
WHERE nombre=’Pedro_Perez’);
COMMIT; -- or END TRANSACTION
-- Mi diseño original era creando una vista para hallar el persona_ID
-- de Pedro Perez, pero Postgresql no lo daba por bueno,
-- por eso la vista USER_ID_Pedro_Perez no se usa
Algebra
USER_ID_Pedro_Perez ← Π persona_id σnombre=0 Pedro Perez0 persona
reserva ← reserva −USER_ID_Pedro_Perez ./ reserva
persona ←
persona −USER_ID_Pedro_Perez ./ persona
b)
SQL
-- a) Seleccionar habitaciones con capacidad superior a 200 personas
-- b) Seleccionar habitaciones ocupadas el 14 de junio del 2004 de 13:30
-- c) Seleccionar habitaciones libres
-- d) imprimir segun el formato requerido
--a) habitaciones con capacidad para mas de 200 personas
CREATE VIEW habitaciones_mas_200 AS
5
a 15:3
SELECT habitacion_ID
FROM habitacion
WHERE capacidad > 200;
--habitaciones que pueden alojar a mas de 200 personas ocupadas
-- el miércoles 14 de Julio del 2004 entre las 13:30 y
-- las 15:30
CREATE VIEW habitaciones_ocupadas AS
SELECT reserva.habitacion_ID, email --el atributo email sera
--necesario despues
FROM reserva,habitaciones_mas_200,persona
WHERE ((’2004-08-14 13:30:00’ between comienzo_reserva AND fin_reserva)
OR (’2004-08-14 15:30:00’ between comienzo_reserva AND fin_reserva))
AND reserva.habitacion_ID=habitaciones_mas_200.habitacion_ID
AND reserva.persona_ID=persona.persona_ID;
--habitaciones libres
CREATE VIEW habitaciones_libres AS
SELECT habitacion_ID
FROM(
SELECT habitacion_ID
FROM habitaciones_mas_200
EXCEPT
SELECT habitacion_ID
FROM habitaciones_ocupadas
) AS alias_inutil
;
--Resultado Final
SELECT habitacion_ID, email
FROM
(
(SELECT habitacion_ID, ’libre’ AS email, ’0’ AS orden
FROM habitaciones_libres
UNION --OJO: union no garantiza ningun ordenamiento
SELECT habitacion_ID,email, ’1’ AS orden
FROM habitaciones_ocupadas
) ORDER BY order
) AS otro_alias_inutil;
Algebra
habitaciones_mas_200 ← Πhabitacion_id σcapacidad>200habitacion
A ← reserva ./ habitaciones_mas_200 ./ persona
habitaciones_ocupadas ← Πreserva.habitacion_id,email σ (2004 − 08 − 14 13 : 30 00 0 > comienzoreserva A
AND 2004 − 08 − 14 13 : 30 < f inreserva)
OR (2004 − 08 − 1415 : 30 > comienzoreserva
AND 2004 − 08 − 14 15 : 30 < f inreserva)
B ← Πhabitacion_id habitaciones_mas_200
C ← Πhabitacion_id habitaciones_ocupadas
habitaciones_libres ← B −C
D ← ρ(order) 0 00
E
← ρ(order) 0 10
6
DD ← ρ(email) 0 email 0
DDD ← Πhabitacion_id,email,order habitaciones_libres × D × DDds f d f s
EEE ← Πhabitacion_id,email,order habitaciones_ocupadas × E
Πhabitacion_id,email DDD ∪ EEE
Nota 1: ’0’, ’1’, ’email’ son relaciones compuestas por una tupla y con un solo atributo,
el valor de la tupla es 0, 1 y email respectivamente.
Nota 2: En principio no existe la operacion ordenar en algebra relacional, una relacion
por definición no tiene order y la salida de todos los operadores de algebra lineal que hemos
visto son relaciones.
Programa (vease fichero adjunto solucion1.pgc)
Ultima pregunta
Usando sql estandard no es posible que una base de datos interaccione con el exterior
(mande mails, imprima etc). Una posibilidad seria crear una tabla “email”
DROP SEQUENCE email_id_seq;
CREATE SEQUENCE email_id_seq
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;
SELECT setval(’email_id_seq’,0);
CREATE TABLE email (
id integer DEFAULT nextval(’email_id_seq’::text) NOT NULL,
"To" text NOT NULL,
"From" text NOT NULL,
"Subject" text NOT NULL,
"Message" text NOT NULL,
"Create_ON" date DEFAULT (’now’::text)::date,
PRIMARY KEY (id)
);
que se rellenaria automaticamente cada vez que se cancele una reserva mediante un trigger:
CREATE TRIGGER init_send_mail
AFTER DELETE ON reserva
FOR EACH ROW
EXECUTE PROCEDURE init_send_mail_fun();
CREATE FUNCTION init_send_mail_fun() RETURNS
-- aqui habria comandos para rellenar la tabla email
--"trigger"
AS ’
RETURN NEW;
END;
’
LANGUAGE plpgsql;
Finalmente un procedimiento externo gobernado por un “cron” deberia conectarse a la
base, leer la tabla de mails, enviarlos y borrar las entradas en la tabla. (Adjunto un ejemplo
de procedimiento externo escrito en python, vease fichero sendmail.py)
7
Descargar