Módulo 19: Casos prácticos E/R
Objetivos del módulo
- Aplicar de forma integrada todo lo aprendido en el bloque de diseño conceptual
- Resolver supuestos completos tipo examen de dificultad creciente
- Justificar cada decisión: por qué algo es entidad, atributo o relación, y por qué tiene esas cardinalidades
- Usar con soltura entidades débiles, jerarquías, relaciones reflexivas, ternarias, agregaciones y relaciones con repetición
- Documentar las restricciones que el diagrama no expresa y las decisiones discutibles
- Deducir el modelo E/R del esquema EMPRESA que se usa en todos los ejemplos de SQL del curso
1. Cómo están planteados los casos
Cada caso sigue la misma estructura, que es la que deberías seguir tú en un examen o en un proyecto:
- Enunciado, redactado como en un examen.
- Análisis: entidades, atributos y relaciones identificados, con su justificación.
- Diagrama E/R completo en la notación del curso (dividido en partes si es grande; las entidades que aparecen en varias partes solo muestran sus atributos una vez).
- Restricciones que no se pueden expresar en el diagrama.
- Decisiones discutibles y alternativas igualmente válidas.
| Caso | Dominio | Dificultad | Conceptos que destaca |
|---|---|---|---|
| 1 | Biblioteca municipal | ★☆☆☆☆ | Entidad débil, N:M con repetición, atributos multivaluados |
| 2 | Centro de formación profesional | ★★☆☆☆ | Ternaria, relación 1:1, historia académica |
| 3 | Clínica hospitalaria | ★★☆☆☆ | Jerarquía, débil, citas e ingresos |
| 4 | Liga deportiva | ★★★☆☆ | Ternaria con temporada, papeles, exclusión, débil |
| 5 | Plataforma de streaming | ★★★☆☆ | Jerarquía, débiles encadenadas, exclusividad |
| 6 | Agencia de viajes | ★★★★☆ | Generalización, débil, agregación |
| 7 | Taller mecánico | ★★★★☆ | Reflexiva, reificación, 1:1 opcional, historia |
| 8 | Red social | ★★★★★ | Varias reflexivas, jerarquía multinivel, exclusión |
| 9 | Esquema EMPRESA del curso | ★★★☆☆ | Ingeniería inversa: del SQL al E/R |
💡 Consejo: Antes de leer cada solución, intenta resolver el caso tú. Lee el enunciado, dibuja tu diagrama en papel y solo entonces compáralo. No te preocupes si no coincide exactamente: en diseño conceptual suele haber varias soluciones correctas. Lo importante es que la tuya recoja todos los requisitos y que sepas justificar las diferencias.
Recuerda la convención de cardinalidades del curso: la pareja (mín, máx) escrita junto a la entidad A indica con cuántas ocurrencias de A se relaciona una ocurrencia de la otra entidad. En las relaciones ternarias, la pareja junto a A indica con cuántas ocurrencias de A se relaciona cada pareja de ocurrencias de las otras dos entidades.
2. Caso 1: Biblioteca municipal
2.1. Enunciado
La red de bibliotecas de un ayuntamiento quiere gestionar su fondo y sus préstamos.
De cada libro se conoce su ISBN, título, año de publicación, número de páginas y la editorial que lo publica (de la que se guarda un código, nombre y ciudad). Un libro puede tener uno o varios autores (código, nombre y nacionalidad) y un autor puede haber escrito varios libros. Cada libro se clasifica en uno o varios temas (código y descripción).
De cada libro, la biblioteca tiene uno o varios ejemplares. Los ejemplares de un libro se numeran correlativamente (1, 2, 3…) y de cada uno se guarda la fecha de adquisición y su estado (bueno, deteriorado, perdido).
Los socios tienen un número de carné, DNI, nombre, dirección, email y uno o varios teléfonos. Un socio puede tomar prestados ejemplares; de cada préstamo se registra la fecha de préstamo, la fecha prevista de devolución y la fecha real de devolución. Un socio puede tomar prestado el mismo ejemplar varias veces a lo largo del tiempo. Si un socio devuelve un ejemplar con retraso, queda sancionado hasta una fecha determinada.
2.2. Análisis
| Elemento | Decisión | Justificación |
|---|---|---|
| LIBRO | Entidad fuerte, id isbn | La obra; tiene datos propios |
| EDITORIAL | Entidad | Tiene código, nombre y ciudad (atributos propios) |
| AUTOR | Entidad | Datos propios; N:M con libro |
| TEMA | Entidad | Tiene código y descripción; N:M con libro |
| EJEMPLAR | Entidad débil de LIBRO, id parcial num_ejemplar | “Se numeran correlativamente” dentro de cada libro: el ejemplar 3 de un libro y el 3 de otro son distintos |
| SOCIO | Entidad fuerte, id num_carne | dni como clave alternativa |
| teléfono | Atributo multivaluado | “uno o varios teléfonos” sin datos propios |
| PRESTAMO | Entidad (reificación) | “El mismo ejemplar varias veces”: una relación N:M SOCIO–EJEMPLAR no admitiría repetir el par |
| sanción | Atributo opcional sancionado_hasta de SOCIO | Solo interesa la fecha de fin de la sanción vigente |
| escribe | Relación N:M LIBRO–AUTOR | “uno o varios autores” / “varios libros” |
| publica | Relación 1:N EDITORIAL–LIBRO | Cada libro, una editorial |
2.3. Diagrama
Parte 1: catálogo.
flowchart TB
classDef debil stroke-width:4px
LIBRO[LIBRO] --- l_isbn([<u>isbn</u>])
LIBRO --- l_tit([titulo])
LIBRO --- l_anio([anio])
LIBRO --- l_pag([num_paginas])
LIBRO ---|"(0,n)"| publica{"publica<br/>1:N"}
publica ---|"(1,1)"| EDITORIAL[EDITORIAL]
EDITORIAL --- e_cod([<u>cod_editorial</u>])
EDITORIAL --- e_nom([nombre])
EDITORIAL --- e_ciu([ciudad])
LIBRO ---|"(1,n)"| escribe{"escribe<br/>N:M"}
escribe ---|"(1,n)"| AUTOR[AUTOR]
AUTOR --- a_cod([<u>cod_autor</u>])
AUTOR --- a_nom([nombre])
AUTOR --- a_nac([nacionalidad])
LIBRO ---|"(0,n)"| trata{"trata<br/>N:M"}
trata ---|"(1,n)"| TEMA[TEMA]
TEMA --- t_cod([<u>cod_tema</u>])
TEMA --- t_des([descripcion])
LIBRO ---|"(1,1)"| tiene{"tiene<br/>ID"}:::debil
tiene ---|"(1,n)"| EJEMPLAR[EJEMPLAR]:::debil
EJEMPLAR --- ej_num(["<u>num_ejemplar</u> parcial"])
EJEMPLAR --- ej_fec([fecha_adquisicion])
EJEMPLAR --- ej_est([estado])
Parte 2: préstamos.
flowchart TB
SOCIO[SOCIO] --- s_num([<u>num_carne</u>])
SOCIO --- s_dni(["dni (AK)"])
SOCIO --- s_nom([nombre])
SOCIO --- s_dir([direccion])
SOCIO --- s_ema([email])
SOCIO --- s_tel(((telefono)))
SOCIO --- s_san(["sancionado_hasta (0,1)"])
SOCIO ---|"(1,1)"| solicita{"solicita<br/>1:N"}
solicita ---|"(0,n)"| PRESTAMO[PRESTAMO]
PRESTAMO --- p_num([<u>num_prestamo</u>])
PRESTAMO --- p_fec([fecha_prestamo])
PRESTAMO --- p_pre([fecha_prevista])
PRESTAMO --- p_dev(["fecha_devolucion (0,1)"])
PRESTAMO ---|"(0,n)"| de{"de<br/>1:N"}
de ---|"(1,1)"| EJEMPLAR[EJEMPLAR]
Lectura de algunas cardinalidades:
escribe: un libro tiene 1..n autores (junto a AUTOR); un autor ha escrito 1..n libros (junto a LIBRO: solo se registran autores con algún libro en el fondo).tiene: un libro tiene 1..n ejemplares (si un libro está en el catálogo es porque hay al menos un ejemplar); cada ejemplar es de exactamente un libro.de: cada préstamo es de exactamente un ejemplar; un ejemplar ha tenido 0..n préstamos.
2.4. Restricciones
- R1: estado ∈ {‘BUENO’, ‘DETERIORADO’, ‘PERDIDO’}.
- R2: fecha_prevista > fecha_prestamo; fecha_devolucion ≥ fecha_prestamo.
- R3: un ejemplar no puede estar en dos préstamos sin devolver a la vez.
- R4: no se presta un ejemplar en estado ‘PERDIDO’.
- R5: un socio con
sancionado_hastaposterior a la fecha actual no puede tomar préstamos. - R6: al registrar una devolución con fecha_devolucion > fecha_prevista, se establece
sancionado_hasta(regla de negocio a definir: por ejemplo, tantos días como días de retraso). - R7: el año de publicación no es posterior al año de adquisición de sus ejemplares.
2.5. Decisiones discutibles
- Préstamo como entidad fuerte o débil. Se ha dado un identificador propio (
num_prestamo). También sería correcto hacerlo débil del ejemplar confecha_prestamocomo identificador parcial (un ejemplar no se presta dos veces el mismo día). - Sanción. Si interesara el historial de sanciones (cuántas veces ha sido sancionado un socio, por qué préstamo), habría que crear una entidad
SANCIONrelacionada conPRESTAMO. Además,sancionado_hastaes en parte derivable de los préstamos devueltos con retraso; se mantiene como dato porque la regla de cálculo la decide la biblioteca y puede tener excepciones. - Red de bibliotecas. El enunciado habla de una “red”. Si hubiera varias sedes y cada ejemplar perteneciera a una, aparecería una entidad
SEDErelacionada 1:N conEJEMPLAR. Es una buena pregunta para el usuario. - Orden de los autores. En muchas bibliotecas importa quién es el primer autor; se resolvería con un atributo
ordenen la relaciónescribe.
3. Caso 2: Centro de formación profesional
3.1. Enunciado
Un centro de FP quiere gestionar su organización académica.
El centro imparte ciclos formativos (código, nombre, familia profesional, grado medio o superior). Cada ciclo se compone de módulos profesionales (código, nombre, horas, curso en el que se imparte: 1º o 2º); cada módulo pertenece a un único ciclo.
Los profesores (DNI, nombre, email, especialidad) pertenecen a un departamento (código, nombre). Cada departamento tiene un jefe de departamento, que es uno de sus profesores.
Cada curso académico (2025-26, 2026-27…) se forman grupos (por ejemplo, “1º DAM A 2026-27”), de los que se guarda un código, el ciclo, el curso (1º o 2º), el curso académico y el turno (mañana o tarde). Cada grupo tiene un profesor tutor, y un profesor puede ser tutor de un solo grupo en cada curso académico.
Cada módulo, en cada grupo, lo imparte un único profesor; un profesor puede impartir varios módulos en el mismo grupo y el mismo módulo en varios grupos.
Los alumnos (NIA, DNI, nombre, fecha de nacimiento, email) se matriculan cada curso académico en un grupo y en uno o varios módulos (un alumno puede repetir un módulo en otro curso académico). Para cada matrícula de un alumno en un módulo en un curso académico se guardan las calificaciones de cada evaluación (1ª, 2ª, final ordinaria, final extraordinaria).
3.2. Análisis
| Elemento | Decisión | Justificación |
|---|---|---|
| CICLO, MODULO | Entidades; MODULO 1:N con CICLO | Cada módulo pertenece a un ciclo |
| DEPARTAMENTO, PROFESOR | Entidades; pertenece 1:N; dirige 1:1 | El jefe es “uno de sus profesores” |
| GRUPO | Entidad fuerte, id cod_grupo | Incluye curso académico y turno |
| imparte | Ternaria PROFESOR–MODULO–GRUPO | El hecho “Pérez imparte Bases de Datos en 1º DAM A” involucra a los tres a la vez |
| tutoriza | Relación 1:N PROFESOR–GRUPO | Cada grupo, un tutor; un profesor, como mucho un grupo por curso académico (restricción) |
| ALUMNO | Entidad, id nia | — |
| MATRICULA | Entidad | Un alumno puede repetir un módulo en otro curso: relación con repetición |
| CALIFICACION | Entidad débil de MATRICULA, id parcial evaluacion | Varias notas por matrícula, una por evaluación |
| curso académico | Atributo | Solo es un valor (“2026-27”); no tiene datos propios |
3.3. Diagrama
Parte 1: organización.
flowchart TB
CICLO[CICLO] --- c_cod([<u>cod_ciclo</u>])
CICLO --- c_nom([nombre])
CICLO --- c_fam([familia])
CICLO --- c_gra([grado])
MODULO[MODULO] --- m_cod([<u>cod_modulo</u>])
MODULO --- m_nom([nombre])
MODULO --- m_hor([horas])
MODULO --- m_cur([curso])
MODULO ---|"(1,n)"| compone{"compone<br/>1:N"}
compone ---|"(1,1)"| CICLO
PROFESOR[PROFESOR] --- p_dni([<u>dni</u>])
PROFESOR --- p_nom([nombre])
PROFESOR --- p_ema([email])
PROFESOR --- p_esp([especialidad])
PROFESOR ---|"(1,n)"| pertenece{"pertenece<br/>1:N"}
pertenece ---|"(1,1)"| DEPARTAMENTO[DEPARTAMENTO]
DEPARTAMENTO --- d_cod([<u>cod_dep</u>])
DEPARTAMENTO --- d_nom([nombre])
PROFESOR ---|"(1,1) jefe"| dirige{"dirige<br/>1:1"}
dirige ---|"(0,1)"| DEPARTAMENTO
GRUPO[GRUPO] --- g_cod([<u>cod_grupo</u>])
GRUPO --- g_cur([curso])
GRUPO --- g_aca([curso_academico])
GRUPO --- g_tur([turno])
GRUPO ---|"(0,n)"| de_ciclo{"de<br/>1:N"}
de_ciclo ---|"(1,1)"| CICLO
PROFESOR ---|"(1,1) tutor"| tutoriza{"tutoriza<br/>1:N"}
tutoriza ---|"(0,n)"| GRUPO
Parte 2: docencia (ternaria).
flowchart TB
PROFESOR[PROFESOR] ---|"(1,1)"| imparte{"imparte<br/>1:N:M"}
MODULO[MODULO] ---|"(1,n)"| imparte
GRUPO[GRUPO] ---|"(1,n)"| imparte
Cálculo de las cardinalidades de la ternaria, fijando parejas:
- Junto a PROFESOR (1,1): para una pareja (módulo, grupo) que se imparte, ¿cuántos profesores? Exactamente uno (“cada módulo, en cada grupo, lo imparte un único profesor”).
- Junto a MODULO (1,n): para una pareja (profesor, grupo) que participa en la relación, ¿cuántos módulos? Uno o varios (“puede impartir varios módulos en el mismo grupo”).
- Junto a GRUPO (1,n): para una pareja (profesor, módulo) que participa, ¿cuántos grupos? Uno o varios (“el mismo módulo en varios grupos”).
Parte 3: matrícula y calificaciones.
flowchart TB
classDef debil stroke-width:4px
ALUMNO[ALUMNO] --- a_nia([<u>nia</u>])
ALUMNO --- a_dni(["dni (AK)"])
ALUMNO --- a_nom([nombre])
ALUMNO --- a_fn([fecha_nacimiento])
ALUMNO --- a_ema([email])
ALUMNO ---|"(1,1)"| hace{"hace<br/>1:N"}
hace ---|"(1,n)"| MATRICULA[MATRICULA]
MATRICULA --- ma_id([<u>num_matricula</u>])
MATRICULA --- ma_fec([fecha_matricula])
MATRICULA ---|"(0,n)"| en_grupo{"en<br/>1:N"}
en_grupo ---|"(1,1)"| GRUPO[GRUPO]
MATRICULA ---|"(0,n)"| de_mod{"de<br/>1:N"}
de_mod ---|"(1,1)"| MODULO[MODULO]
MATRICULA ---|"(1,1)"| obtiene{"obtiene<br/>ID"}:::debil
obtiene ---|"(0,n)"| CALIFICACION[CALIFICACION]:::debil
CALIFICACION --- ca_ev(["<u>evaluacion</u> parcial"])
CALIFICACION --- ca_not([nota])
Aquí MATRICULA representa la matrícula de un alumno en un módulo dentro de un grupo (y, por tanto, en un curso académico). Así un alumno que repite “Programación” tiene dos matrículas distintas, en grupos de cursos académicos distintos.
3.4. Restricciones
- R1: grado ∈ {‘MEDIO’, ‘SUPERIOR’}; curso ∈ {1, 2}; turno ∈ {‘MAÑANA’, ‘TARDE’}; evaluacion ∈ {‘1EV’, ‘2EV’, ‘FO’, ‘FE’}; nota entre 0 y 10.
- R2: el jefe de un departamento pertenece a ese departamento (ciclo PROFESOR–DEPARTAMENTO con caminos coincidentes).
- R3: un profesor es tutor como mucho de un grupo por curso académico.
- R4: el módulo de una matrícula pertenece al ciclo del grupo de la matrícula y al mismo curso (1º o 2º) que el grupo.
- R5: un alumno no se matricula dos veces del mismo módulo en el mismo curso académico.
- R6: en la ternaria
imparte, el módulo pertenece al ciclo del grupo. - R7: solo hay calificación ‘FE’ si la de ‘FO’ es inferior a 5.
3.5. Decisiones discutibles
- Curso académico como entidad. Si de cada curso académico interesaran datos (fechas de inicio y fin, calendario de evaluaciones), sería una entidad
CURSO_ACADEMICOrelacionada conGRUPO. - MATRICULA por módulo o por curso. Otra opción, más fiel al proceso administrativo, es una entidad
MATRICULA(alumno + curso académico + grupo) con una relación N:M conMODULO, cuya ocurrencia (matrícula, módulo) tiene las calificaciones. Requeriría una agregación o convertir esa N:M en otra entidad débil. La solución elegida es más simple. - Ternaria frente a binarias. ¿Se podría sustituir
impartepor dos binarias PROFESOR–MODULO y MODULO–GRUPO? No: si Pérez y López imparten los dos “Bases de Datos” y el módulo se da en los grupos A y B, las binarias no dicen quién da clase en cada grupo. Sí se podría modelar como entidadDOCENCIA(módulo + grupo) con una relación 1:N con PROFESOR, equivalente a la ternaria con (1,1) junto a PROFESOR. - Relación 1:1
dirige. La pareja (1,1) junto a PROFESOR indica que todo departamento tiene jefe; (0,1) junto a DEPARTAMENTO, que un profesor dirige como mucho un departamento.
4. Caso 3: Clínica hospitalaria
4.1. Enunciado
Una clínica privada quiere gestionar su actividad asistencial.
Del personal de la clínica se guarda número de empleado, DNI, nombre, teléfono y fecha de contratación. El personal es médico, enfermero o administrativo (cada persona es exactamente una de las tres cosas). De los médicos se guarda el número de colegiado y su especialidad (código, nombre); cada especialidad tiene un médico que es jefe de servicio. Los enfermeros están asignados a una planta. Los administrativos no tienen datos adicionales.
La clínica tiene varias plantas (número, descripción) y en cada planta hay habitaciones numeradas (101, 102… en la planta 1; 201… en la planta 2) con un número de camas.
De los pacientes se guarda número de historia clínica, DNI (si lo tienen), nombre, fecha de nacimiento, dirección y, opcionalmente, la aseguradora médica (código, nombre) y el número de póliza.
Los pacientes piden citas con un médico para una fecha y hora; un paciente puede tener muchas citas con el mismo médico. Si el paciente acude, la cita genera una consulta en la que se registran observaciones, uno o varios diagnósticos (de un catálogo con código CIE y descripción) y, si procede, la prescripción de medicamentos (código nacional, nombre) con la posología de cada uno.
Cuando un paciente es ingresado, se registra la fecha de ingreso, la de alta, la habitación y el médico responsable del ingreso.
4.2. Análisis
| Elemento | Decisión | Justificación |
|---|---|---|
| PERSONAL → MEDICO, ENFERMERO, ADMINISTRATIVO | Jerarquía (total, exclusiva) | “Exactamente una de las tres cosas” |
| ADMINISTRATIVO | No se dibuja como subtipo | No tiene atributos ni relaciones propios; la cobertura pasa a parcial |
| ESPECIALIDAD | Entidad | Tiene código, nombre y jefe |
| PLANTA, HABITACION | HABITACION débil de PLANTA | Numeración dentro de la planta (101 es la 1 de la planta 1) |
| PACIENTE | Entidad, id num_historia | El DNI es opcional (menores, extranjeros): no puede ser identificador |
| ASEGURADORA | Entidad; relación 1:N opcional con atributo num_poliza | Tiene datos propios |
| CITA | Entidad | Repetible con el mismo médico |
| CONSULTA | Entidad débil de CITA (1:1 de identificación) | Solo existe si la cita se realiza |
| DIAGNOSTICO | Entidad de catálogo; N:M con CONSULTA | — |
| prescribe | N:M CONSULTA–MEDICAMENTO con posologia | — |
| INGRESO | Entidad | Un paciente puede ingresar varias veces en la misma habitación |
4.3. Diagrama
Parte 1: personal y estructura.
flowchart TB
classDef debil stroke-width:4px
PERSONAL[PERSONAL] --- pe_num([<u>num_empleado</u>])
PERSONAL --- pe_dni(["dni (AK)"])
PERSONAL --- pe_nom([nombre])
PERSONAL --- pe_tel([telefono])
PERSONAL --- pe_fec([fecha_contratacion])
PERSONAL --- pe_cat([categoria])
PERSONAL ---|"categoria"| jp{{"es un<br/>(parcial, exclusiva)"}}
jp ---|"MEDICO"| MEDICO[MEDICO]
jp ---|"ENFERMERO"| ENFERMERO[ENFERMERO]
MEDICO --- me_col([num_colegiado])
MEDICO ---|"(0,n)"| tiene_esp{"tiene<br/>1:N"}
tiene_esp ---|"(1,1)"| ESPECIALIDAD[ESPECIALIDAD]
ESPECIALIDAD --- es_cod([<u>cod_especialidad</u>])
ESPECIALIDAD --- es_nom([nombre])
MEDICO ---|"(1,1) jefe"| jefe_de{"es_jefe<br/>1:1"}
jefe_de ---|"(0,1)"| ESPECIALIDAD
ENFERMERO ---|"(1,n)"| asignado{"asignado<br/>1:N"}
asignado ---|"(1,1)"| PLANTA[PLANTA]
PLANTA --- pl_num([<u>num_planta</u>])
PLANTA --- pl_des([descripcion])
PLANTA ---|"(1,1)"| contiene{"contiene<br/>ID"}:::debil
contiene ---|"(1,n)"| HABITACION[HABITACION]:::debil
HABITACION --- ha_num(["<u>num_habitacion</u> parcial"])
HABITACION --- ha_cam([num_camas])
Parte 2: actividad asistencial.
flowchart TB
classDef debil stroke-width:4px
PACIENTE[PACIENTE] --- pa_his([<u>num_historia</u>])
PACIENTE --- pa_dni(["dni (0,1) (AK)"])
PACIENTE --- pa_nom([nombre])
PACIENTE --- pa_fn([fecha_nacimiento])
PACIENTE --- pa_dir([direccion])
PACIENTE ---|"(0,n)"| asegura{"asegurado<br/>1:N"}
asegura ---|"(0,1)"| ASEGURADORA[ASEGURADORA]
asegura --- as_pol([num_poliza])
ASEGURADORA --- ase_cod([<u>cod_aseguradora</u>])
PACIENTE ---|"(1,1)"| pide{"pide<br/>1:N"}
pide ---|"(0,n)"| CITA[CITA]
CITA --- ci_num([<u>num_cita</u>])
CITA --- ci_fh([fecha_hora])
CITA ---|"(0,n)"| con{"con<br/>1:N"}
con ---|"(1,1)"| MEDICO[MEDICO]
CITA ---|"(1,1)"| genera{"genera<br/>ID"}:::debil
genera ---|"(0,1)"| CONSULTA[CONSULTA]:::debil
CONSULTA --- co_obs([observaciones])
CONSULTA ---|"(0,n)"| diagnostica{"diagnostica<br/>N:M"}
diagnostica ---|"(1,n)"| DIAGNOSTICO[DIAGNOSTICO]
DIAGNOSTICO --- di_cie([<u>codigo_cie</u>])
DIAGNOSTICO --- di_des([descripcion])
CONSULTA ---|"(0,n)"| prescribe{"prescribe<br/>N:M"}
prescribe ---|"(0,n)"| MEDICAMENTO[MEDICAMENTO]
prescribe --- pr_pos([posologia])
MEDICAMENTO --- med_cod([<u>codigo_nacional</u>])
MEDICAMENTO --- med_nom([nombre])
CONSULTA es débil de CITA con una relación de identificación 1:1: se identifica por la misma cita (no necesita identificador parcial). Una cita genera 0 o 1 consultas (si el paciente no acude, ninguna).
Parte 3: ingresos.
flowchart TB
PACIENTE[PACIENTE] ---|"(1,1)"| ingresa{"ingresa<br/>1:N"}
ingresa ---|"(0,n)"| INGRESO[INGRESO]
INGRESO --- in_num([<u>num_ingreso</u>])
INGRESO --- in_fi([fecha_ingreso])
INGRESO --- in_fa(["fecha_alta (0,1)"])
INGRESO ---|"(0,n)"| ocupa{"ocupa<br/>1:N"}
ocupa ---|"(1,1)"| HABITACION[HABITACION]
INGRESO ---|"(0,n)"| responsable{"responsable<br/>1:N"}
responsable ---|"(1,1)"| MEDICO[MEDICO]
4.4. Restricciones
- R1: categoria ∈ {‘MEDICO’, ‘ENFERMERO’, ‘ADMINISTRATIVO’}; los subtipos MEDICO y ENFERMERO corresponden a esos valores.
- R2: el jefe de servicio de una especialidad tiene esa especialidad.
- R3: un médico no puede tener dos citas a la misma fecha_hora.
- R4: fecha_alta ≥ fecha_ingreso.
- R5: el número de ingresos simultáneos (sin alta o con periodos solapados) en una habitación no supera su número de camas.
- R6: un paciente no puede tener dos ingresos con periodos solapados.
- R7: la consulta de una cita solo puede registrarse en la fecha de la cita o después.
- R8: el número de póliza solo existe si el paciente tiene aseguradora (es atributo de la relación, así que se cumple por construcción).
4.5. Decisiones discutibles
- Cama como entidad. Si se necesitara saber qué cama concreta ocupa el paciente,
CAMAsería débil deHABITACIONy el ingreso se relacionaría con la cama. - Consulta y cita en una sola entidad. Muchos sistemas tienen una única entidad
CITAcon un estado (‘PENDIENTE’, ‘REALIZADA’, ‘ANULADA’) y los datos de la consulta como atributos opcionales. Es una simplificación válida; separarlas deja más claro qué datos son obligatorios en cada caso. - Diagnósticos en los ingresos. Los ingresos también suelen tener diagnósticos (de ingreso y de alta). Habría que añadir otra N:M INGRESO–DIAGNOSTICO, o generalizar CONSULTA e INGRESO en un supertipo
EPISODIO_ASISTENCIALque tenga la relación con DIAGNOSTICO. - Médico de la cita y médico de la consulta. Se ha supuesto que la consulta la atiende el médico de la cita. Si puede atenderla un sustituto, la consulta necesitaría su propia relación con MEDICO (y el ciclo resultante no sería redundante).
5. Caso 4: Liga deportiva
5.1. Enunciado
Una federación de baloncesto quiere gestionar su liga.
La liga se organiza por temporadas (por ejemplo, “2026-27”, con fecha de inicio y fin). Los equipos (código, nombre, ciudad, año de fundación) juegan como local en un pabellón (código, nombre, aforo); varios equipos pueden compartir pabellón.
Los jugadores (número de licencia, nombre, fecha de nacimiento, nacionalidad, altura) firman una ficha con un equipo para una temporada, con un dorsal y un salario. En una temporada, un jugador solo puede tener ficha con un equipo; puede volver a un equipo en el que ya jugó.
Cada temporada tiene varias jornadas. En cada partido se enfrentan un equipo local y uno visitante; se guarda la jornada, la fecha y hora, el resultado (puntos de cada equipo) y el pabellón en el que se juega (normalmente el del local, pero no siempre). Cada partido lo dirigen tres árbitros (número de colegiado, nombre, categoría): uno como principal y dos como auxiliares.
De cada partido interesa la estadística individual de cada jugador que ha participado: minutos jugados, puntos, rebotes y asistencias.
5.2. Análisis
| Elemento | Decisión | Justificación |
|---|---|---|
| TEMPORADA | Entidad | Tiene fechas; participa en la ficha y en los partidos |
| EQUIPO, PABELLON, JUGADOR, ARBITRO | Entidades | Datos propios |
| ficha | Ternaria JUGADOR–EQUIPO–TEMPORADA con dorsal y salario | El hecho involucra a los tres; permite volver a un equipo en otra temporada |
| PARTIDO | Entidad, id num_partido | Acontecimiento con muchos datos |
| local / visitante | Dos relaciones 1:N EQUIPO–PARTIDO con exclusión | Dos papeles distintos del equipo en el partido |
| arbitra | N:M ARBITRO–PARTIDO con atributo funcion | Tres árbitros por partido, cada uno con una función |
| estadística | N:M JUGADOR–PARTIDO con atributos | Un jugador juega un partido como mucho una vez |
5.3. Diagrama
Parte 1: equipos, jugadores y fichas.
flowchart TB
TEMPORADA[TEMPORADA] --- t_cod([<u>cod_temporada</u>])
TEMPORADA --- t_ini([fecha_inicio])
TEMPORADA --- t_fin([fecha_fin])
EQUIPO[EQUIPO] --- e_cod([<u>cod_equipo</u>])
EQUIPO --- e_nom([nombre])
EQUIPO --- e_ciu([ciudad])
EQUIPO --- e_anio([anio_fundacion])
EQUIPO ---|"(0,n)"| juega_en{"juega_en<br/>1:N"}
juega_en ---|"(1,1)"| PABELLON[PABELLON]
PABELLON --- pb_cod([<u>cod_pabellon</u>])
PABELLON --- pb_nom([nombre])
PABELLON --- pb_afo([aforo])
JUGADOR[JUGADOR] --- j_lic([<u>num_licencia</u>])
JUGADOR --- j_nom([nombre])
JUGADOR --- j_fn([fecha_nacimiento])
JUGADOR --- j_nac([nacionalidad])
JUGADOR --- j_alt([altura])
JUGADOR ---|"(1,n)"| ficha{"ficha<br/>1:N:M"}
EQUIPO ---|"(1,1)"| ficha
TEMPORADA ---|"(1,n)"| ficha
ficha --- f_dor([dorsal])
ficha --- f_sal([salario])
Cardinalidades de la ternaria ficha, fijando parejas:
- Junto a EQUIPO (1,1): para una pareja (jugador, temporada), ¿con cuántos equipos tiene ficha? Exactamente uno (si tiene ficha esa temporada).
- Junto a JUGADOR (1,n): para una pareja (equipo, temporada), ¿cuántos jugadores? Varios (la plantilla).
- Junto a TEMPORADA (1,n): para una pareja (jugador, equipo), ¿cuántas temporadas? Una o varias, no necesariamente consecutivas.
Parte 2: partidos.
flowchart TB
EQUIPO[EQUIPO] ---|"(1,1) local"| local{"local<br/>1:N"}
local ---|"(0,n)"| PARTIDO[PARTIDO]
EQUIPO ---|"(1,1) visitante"| visitante{"visitante<br/>1:N"}
visitante ---|"(0,n)"| PARTIDO
local -.-|"exclusión"| visitante
PARTIDO --- pa_num([<u>num_partido</u>])
PARTIDO --- pa_jor([jornada])
PARTIDO --- pa_fh([fecha_hora])
PARTIDO --- pa_pl(["puntos_local (0,1)"])
PARTIDO --- pa_pv(["puntos_visitante (0,1)"])
PARTIDO ---|"(0,n)"| de_temp{"de<br/>1:N"}
de_temp ---|"(1,1)"| TEMPORADA[TEMPORADA]
PARTIDO ---|"(0,n)"| se_juega{"se_juega<br/>1:N"}
se_juega ---|"(1,1)"| PABELLON[PABELLON]
ARBITRO[ARBITRO] ---|"(3,3)"| arbitra{"arbitra<br/>N:M"}
arbitra ---|"(0,n)"| PARTIDO
arbitra --- ar_fun([funcion])
ARBITRO --- ar_col([<u>num_colegiado</u>])
ARBITRO --- ar_nom([nombre])
ARBITRO --- ar_cat([categoria])
JUGADOR[JUGADOR] ---|"(0,n)"| participa{"participa<br/>N:M"}
participa ---|"(0,n)"| PARTIDO
participa --- pt_min([minutos])
participa --- pt_pun([puntos])
participa --- pt_reb([rebotes])
participa --- pt_asi([asistencias])
La pareja (3,3) junto a ARBITRO expresa que cada partido tiene exactamente tres árbitros.
5.4. Restricciones
- R1: el equipo local y el visitante de un partido son distintos (la exclusión lo expresa).
- R2: en cada partido, exactamente un árbitro tiene funcion = ‘PRINCIPAL’ y dos ‘AUXILIAR’.
- R3: la fecha del partido está entre las fechas de inicio y fin de su temporada.
- R4: un jugador que participa en un partido tiene ficha, en la temporada del partido, con el equipo local o con el visitante (restricción de caminos entre
participa,ficha,localyvisitante). - R5: un equipo juega como mucho un partido por jornada.
- R6: en una temporada, dos jugadores del mismo equipo no tienen el mismo dorsal.
- R7: puntos_local y puntos_visitante son nulos si el partido no se ha jugado y no pueden ser iguales al final (en baloncesto no hay empates).
- R8: la suma de los puntos de los jugadores de un equipo en un partido es igual a los puntos de ese equipo (redundancia controlada: los puntos del partido son derivables de la estadística, pero se guardan porque son el dato oficial).
5.5. Decisiones discutibles
- Ficha como entidad. La ternaria podría reificarse en una entidad
FICHA(débil de JUGADOR con la temporada como identificador parcial). Sería necesario si la ficha tuviera relaciones propias (por ejemplo, cláusulas, lesiones durante la ficha) o si se permitieran cambios de equipo a mitad de temporada (entonces haría falta una fecha de alta y la cardinalidad junto a EQUIPO pasaría a (1,n)). - Árbitros con tres relaciones. Otra opción es dibujar tres relaciones (
principal1:N,auxiliar1,auxiliar2). Es peor: duplica la relación y complica la consulta “partidos arbitrados por X”. - Partido como entidad débil de temporada. Podría identificarse por temporada + jornada + equipo local. El identificador artificial es más cómodo.
- Resultado derivado. Si se considera que los puntos del partido son siempre la suma de los individuales,
puntos_localypuntos_visitanteserían atributos derivados.
6. Caso 5: Plataforma de streaming
6.1. Enunciado
Una plataforma de vídeo bajo demanda quiere rediseñar su base de datos.
Los clientes se registran con una cuenta (email, contraseña cifrada, país, fecha de alta). Cada cuenta está suscrita a un plan (código, nombre, precio mensual, número máximo de perfiles, calidad máxima); interesa conservar la historia de los planes que ha tenido cada cuenta con sus fechas.
Cada cuenta tiene uno o varios perfiles (nombre, si es infantil, idioma preferido); los perfiles de una cuenta se distinguen por su nombre.
El catálogo está formado por contenidos (código, título, año, clasificación por edades, sinopsis), que son películas (con duración) o series. Las series se organizan en temporadas (número 1, 2, 3…) y cada temporada en episodios (número dentro de la temporada, título, duración). Cada contenido pertenece a uno o varios géneros. De cada contenido interesan las personas que han participado (código, nombre), indicando su papel (director, actor, guionista…); una misma persona puede tener varios papeles en el mismo contenido (actor y director).
Cada perfil puede valorar un contenido (de 1 a 5, una sola vez por contenido) y añadirlo a su lista. De cada reproducción se guarda el perfil, la película o el episodio reproducido, la fecha y hora de inicio, el minuto en el que se detuvo y el dispositivo.
6.2. Análisis
| Elemento | Decisión | Justificación |
|---|---|---|
| CUENTA | Entidad, id email | Único y obligatorio |
| PLAN | Entidad | Datos propios |
| SUSCRIPCION | Entidad débil de CUENTA (historia), id parcial fecha_inicio | Una cuenta puede volver a un plan anterior (repetición) |
| PERFIL | Débil de CUENTA, id parcial nombre | “Se distinguen por su nombre” dentro de la cuenta |
| CONTENIDO → PELICULA, SERIE | Jerarquía (total, exclusiva) | Atributos y relaciones propios |
| TEMPORADA | Débil de SERIE | Numerada dentro de la serie |
| EPISODIO | Débil de TEMPORADA | Numerado dentro de la temporada: débil de débil |
| GENERO | Entidad, N:M con CONTENIDO | — |
| participa | Relación N:M PERSONA–CONTENIDO con atributo papel | Como una persona puede tener varios papeles en el mismo contenido, papel debe formar parte de la identificación: se modela como ternaria con una entidad PAPEL |
| valora, en_lista | N:M PERFIL–CONTENIDO | Una vez por par |
| REPRODUCCION | Entidad | Repetible; es de una película o de un episodio → exclusividad |
6.3. Diagrama
Parte 1: cuentas, planes y perfiles.
flowchart TB
classDef debil stroke-width:4px
CUENTA[CUENTA] --- cu_ema([<u>email</u>])
CUENTA --- cu_pwd([password_hash])
CUENTA --- cu_pai([pais])
CUENTA --- cu_fec([fecha_alta])
CUENTA ---|"(1,1)"| tiene_sus{"tiene<br/>ID"}:::debil
tiene_sus ---|"(1,n)"| SUSCRIPCION[SUSCRIPCION]:::debil
SUSCRIPCION --- su_ini(["<u>fecha_inicio</u> parcial"])
SUSCRIPCION --- su_fin(["fecha_fin (0,1)"])
SUSCRIPCION ---|"(0,n)"| de_plan{"de<br/>1:N"}
de_plan ---|"(1,1)"| PLAN[PLAN]
PLAN --- pl_cod([<u>cod_plan</u>])
PLAN --- pl_nom([nombre])
PLAN --- pl_pre([precio_mensual])
PLAN --- pl_max([max_perfiles])
PLAN --- pl_cal([calidad_max])
CUENTA ---|"(1,1)"| tiene_per{"tiene<br/>ID"}:::debil
tiene_per ---|"(1,n)"| PERFIL[PERFIL]:::debil
PERFIL --- pe_nom(["<u>nombre</u> parcial"])
PERFIL --- pe_inf([infantil])
PERFIL --- pe_idi([idioma])
Parte 2: catálogo.
flowchart TB
classDef debil stroke-width:4px
CONTENIDO[CONTENIDO] --- co_cod([<u>cod_contenido</u>])
CONTENIDO --- co_tit([titulo])
CONTENIDO --- co_anio([anio])
CONTENIDO --- co_cla([clasificacion_edad])
CONTENIDO --- co_sin([sinopsis])
CONTENIDO --- jc{{"es un<br/>(total, exclusiva)"}}
jc --- PELICULA[PELICULA]
jc --- SERIE[SERIE]
PELICULA --- pe_dur([duracion_min])
SERIE ---|"(1,1)"| tiene_t{"tiene<br/>ID"}:::debil
tiene_t ---|"(1,n)"| TEMPORADA[TEMPORADA]:::debil
TEMPORADA --- te_num(["<u>num_temporada</u> parcial"])
TEMPORADA ---|"(1,1)"| tiene_e{"tiene<br/>ID"}:::debil
tiene_e ---|"(1,n)"| EPISODIO[EPISODIO]:::debil
EPISODIO --- ep_num(["<u>num_episodio</u> parcial"])
EPISODIO --- ep_tit([titulo])
EPISODIO --- ep_dur([duracion_min])
CONTENIDO ---|"(0,n)"| clasifica{"clasifica<br/>N:M"}
clasifica ---|"(1,n)"| GENERO[GENERO]
GENERO --- ge_cod([<u>cod_genero</u>])
GENERO --- ge_nom([nombre])
PERSONA[PERSONA] ---|"(1,n)"| participa{"participa<br/>N:M:P"}
CONTENIDO ---|"(1,n)"| participa
PAPEL[PAPEL] ---|"(1,n)"| participa
PERSONA --- ps_cod([<u>cod_persona</u>])
PERSONA --- ps_nom([nombre])
PAPEL --- pa_cod([<u>cod_papel</u>])
PAPEL --- pa_nom([nombre])
Un episodio se identifica por la serie + el número de temporada + su número de episodio: débil de una débil.
Cardinalidades de la ternaria participa, fijando parejas que participan en la relación:
- Junto a PAPEL (1,n): para una pareja (persona, contenido), uno o varios papeles (actor y director a la vez).
- Junto a PERSONA (1,n): para una pareja (contenido, papel), una o varias personas (todo el reparto de actores).
- Junto a CONTENIDO (1,n): para una pareja (persona, papel), uno o varios contenidos (la filmografía de un actor).
⚠️ Importante: En las ternarias, las mínimas se calculan sobre las parejas que participan en la relación, por eso casi siempre son 1 (o 0 si se admite una pareja “incompleta”). El cálculo riguroso de cardinalidades en ternarias se explica en el módulo de relaciones ternarias; en un examen, indica siempre qué convención usas.
Parte 3: actividad de los perfiles.
flowchart TB
PERFIL[PERFIL] ---|"(0,n)"| valora{"valora<br/>N:M"}
valora ---|"(0,n)"| CONTENIDO[CONTENIDO]
valora --- va_pun([puntuacion])
PERFIL ---|"(0,n)"| en_lista{"en_lista<br/>N:M"}
en_lista ---|"(0,n)"| CONTENIDO
en_lista --- el_fec([fecha_anadido])
PERFIL ---|"(1,1)"| reproduce{"reproduce<br/>1:N"}
reproduce ---|"(0,n)"| REPRODUCCION[REPRODUCCION]
REPRODUCCION --- re_id([<u>id_reproduccion</u>])
REPRODUCCION --- re_ini([fecha_hora_inicio])
REPRODUCCION --- re_min([minuto_parada])
REPRODUCCION --- re_dis([dispositivo])
REPRODUCCION ---|"(0,n)"| de_pel{"de_pelicula<br/>1:N"}
de_pel ---|"(0,1)"| PELICULA[PELICULA]
REPRODUCCION ---|"(0,n)"| de_epi{"de_episodio<br/>1:N"}
de_epi ---|"(0,1)"| EPISODIO[EPISODIO]
de_pel -.-|"exclusividad"| de_epi
Cada reproducción es de 0 o 1 películas y de 0 o 1 episodios, con exclusividad total: exactamente una de las dos.
6.4. Restricciones
- R1: puntuacion entre 1 y 5.
- R2: el número de perfiles de una cuenta no supera el
max_perfilesdel plan de su suscripción vigente. - R3: las suscripciones de una cuenta no se solapan y como mucho una está vigente (sin fecha_fin).
- R4: un perfil infantil solo puede reproducir contenidos con clasificacion_edad ≤ 12 (o el umbral que se defina).
- R5: minuto_parada no supera la duración de la película o del episodio reproducido.
- R6: exclusividad total entre
de_peliculayde_episodio. - R7: un contenido es película o serie, no ambos (cobertura de la jerarquía).
6.5. Decisiones discutibles
- Exclusividad o generalización para lo reproducible. Una alternativa elegante es crear un supertipo
ELEMENTO_REPRODUCIBLEcon subtiposPELICULAyEPISODIO(con duración común) y relacionarREPRODUCCIONsolo con el supertipo. PeroPELICULAya es subtipo deCONTENIDO: habría herencia múltiple (una retícula sin raíz común), lo que en realidad apunta a una categoría. La exclusividad es la opción más simple. - Papel como atributo. Si una persona solo pudiera tener un papel por contenido, bastaría con una N:M PERSONA–CONTENIDO con atributo
papel. El enunciado permite varios, por eso la ternaria conPAPEL(o, equivalentemente, un atributopapelque forme parte del identificador de la relación reificada). - Valoraciones de episodios. Se ha supuesto que se valoran contenidos completos (una serie, no un episodio).
- Contraseña. Se guarda el hash, nunca la contraseña: es un requisito de seguridad que conviene reflejar ya en el modelo conceptual.
7. Caso 6: Agencia de viajes
7.1. Enunciado
Una agencia de viajes organiza viajes en grupo.
Los clientes de la agencia tienen un código, una dirección, un teléfono y un email. Pueden ser particulares (DNI, nombre, fecha de nacimiento) o empresas (CIF, razón social, persona de contacto); las empresas tienen además un descuento pactado.
La agencia ofrece viajes de catálogo (código, nombre, descripción, número de días). Cada viaje tiene un itinerario formado por etapas ordenadas (1, 2, 3…); en cada etapa se visita una ciudad (código, nombre, país), se pasan un número de noches y se aloja al grupo en un hotel (código, nombre, categoría, ciudad).
Cada viaje se programa en varias salidas (fechas de salida y regreso, plazas, precio por persona). Cada salida va acompañada de un guía (DNI, nombre, idiomas que habla); un guía puede acompañar muchas salidas, pero no dos a la vez.
Un cliente hace reservas para una salida: de cada reserva se guarda el número, fecha, estado y las personas que viajan (viajeros: DNI, nombre, fecha de nacimiento), que no tienen por qué ser clientes. Durante el viaje, cada viajero de una reserva puede contratar excursiones opcionales (código, nombre, precio) de las que ofrece cada viaje, indicando la fecha de contratación.
7.2. Análisis
| Elemento | Decisión | Justificación |
|---|---|---|
| CLIENTE → PARTICULAR, EMPRESA | Generalización (total, exclusiva) | Identificador común (cod_cliente), atributos comunes y propios |
| VIAJE | Entidad | Producto de catálogo |
| ETAPA | Débil de VIAJE, id parcial orden | Numeradas dentro del viaje |
| CIUDAD, HOTEL | Entidades; HOTEL 1:N con CIUDAD | — |
| SALIDA | Débil de VIAJE, id parcial fecha_salida | Programación concreta de un viaje |
| GUIA | Entidad; idioma multivaluado | — |
| RESERVA | Entidad | De un cliente para una salida |
| VIAJERO | Entidad, id dni | No es necesariamente cliente |
| viaja | N:M RESERVA–VIAJERO | Una reserva, varios viajeros; un viajero, muchas reservas (en distintos viajes) |
| EXCURSION | Entidad; N:M con VIAJE (las que ofrece) | — |
| contrata | Relación entre (viajero en reserva) y EXCURSION → agregación | La excursión no la contrata el viajero “en general” ni la reserva “en general”, sino un viajero dentro de una reserva concreta |
7.3. Diagrama
Parte 1: clientes.
flowchart TB
CLIENTE[CLIENTE] --- c_cod([<u>cod_cliente</u>])
CLIENTE --- c_dir([direccion])
CLIENTE --- c_tel([telefono])
CLIENTE --- c_ema([email])
CLIENTE --- jc{{"es un<br/>(total, exclusiva)"}}
jc --- PARTICULAR[PARTICULAR]
jc --- EMPRESA[EMPRESA]
PARTICULAR --- pa_dni(["dni (AK)"])
PARTICULAR --- pa_nom([nombre])
PARTICULAR --- pa_fn([fecha_nacimiento])
EMPRESA --- em_cif(["cif (AK)"])
EMPRESA --- em_raz([razon_social])
EMPRESA --- em_con([contacto])
EMPRESA --- em_des([descuento])
Parte 2: catálogo, itinerario y salidas.
flowchart TB
classDef debil stroke-width:4px
VIAJE[VIAJE] --- v_cod([<u>cod_viaje</u>])
VIAJE --- v_nom([nombre])
VIAJE --- v_des([descripcion])
VIAJE --- v_dia([num_dias])
VIAJE ---|"(1,1)"| compone{"compone<br/>ID"}:::debil
compone ---|"(1,n)"| ETAPA[ETAPA]:::debil
ETAPA --- et_ord(["<u>orden</u> parcial"])
ETAPA --- et_noc([noches])
ETAPA ---|"(0,n)"| visita{"visita<br/>1:N"}
visita ---|"(1,1)"| CIUDAD[CIUDAD]
ETAPA ---|"(0,n)"| aloja{"aloja<br/>1:N"}
aloja ---|"(1,1)"| HOTEL[HOTEL]
HOTEL ---|"(0,n)"| situado{"situado<br/>1:N"}
situado ---|"(1,1)"| CIUDAD
CIUDAD --- ci_cod([<u>cod_ciudad</u>])
CIUDAD --- ci_nom([nombre])
CIUDAD --- ci_pai([pais])
HOTEL --- ho_cod([<u>cod_hotel</u>])
HOTEL --- ho_nom([nombre])
HOTEL --- ho_cat([categoria])
VIAJE ---|"(1,1)"| programa{"programa<br/>ID"}:::debil
programa ---|"(0,n)"| SALIDA[SALIDA]:::debil
SALIDA --- sa_fs(["<u>fecha_salida</u> parcial"])
SALIDA --- sa_fr([fecha_regreso])
SALIDA --- sa_pla([plazas])
SALIDA --- sa_pre([precio_persona])
GUIA[GUIA] ---|"(1,1)"| acompana{"acompaña<br/>1:N"}
acompana ---|"(0,n)"| SALIDA
GUIA --- gu_dni([<u>dni</u>])
GUIA --- gu_nom([nombre])
GUIA --- gu_idi(((idioma)))
VIAJE ---|"(0,n)"| ofrece{"ofrece<br/>N:M"}
ofrece ---|"(0,n)"| EXCURSION[EXCURSION]
EXCURSION --- ex_cod([<u>cod_excursion</u>])
EXCURSION --- ex_nom([nombre])
EXCURSION --- ex_pre([precio])
Parte 3: reservas, viajeros y excursiones (agregación).
flowchart TB
CLIENTE[CLIENTE] ---|"(1,1)"| hace{"hace<br/>1:N"}
hace ---|"(0,n)"| RESERVA
SALIDA[SALIDA] ---|"(1,1)"| para{"para<br/>1:N"}
para ---|"(0,n)"| RESERVA
subgraph AGR["viaje de un viajero (agregación)"]
RESERVA[RESERVA] ---|"(0,n)"| viaja{"viaja<br/>N:M"}
viaja ---|"(1,n)"| VIAJERO[VIAJERO]
end
RESERVA --- r_num([<u>num_reserva</u>])
RESERVA --- r_fec([fecha])
RESERVA --- r_est([estado])
VIAJERO --- vi_dni([<u>dni</u>])
VIAJERO --- vi_nom([nombre])
VIAJERO --- vi_fn([fecha_nacimiento])
AGR ---|"(0,n)"| contrata{"contrata<br/>N:M"}
contrata ---|"(0,n)"| EXCURSION[EXCURSION]
contrata --- co_fec([fecha_contratacion])
Lectura:
viaja: una reserva tiene 1..n viajeros; un viajero aparece en 0..n reservas.- La agregación encierra
viajacon sus entidades: cada ocurrencia es “el viajero V en la reserva R”. Esa ocurrencia contrata 0..n excursiones, y cada excursión la contratan 0..n viajeros-en-reserva.
7.4. Restricciones
- R1: fecha_regreso − fecha_salida + 1 = num_dias del viaje (derivación; si se cumple siempre,
fecha_regresoes derivado). - R2: la suma de noches de las etapas de un viaje es num_dias − 1.
- R3: el hotel de una etapa está en la ciudad de la etapa (ciclo ETAPA–HOTEL–CIUDAD con caminos coincidentes).
- R4: el número total de viajeros de las reservas no canceladas de una salida no supera sus plazas.
- R5: un guía no acompaña dos salidas con fechas solapadas.
- R6: la excursión contratada está entre las que
ofreceel viaje de la salida de la reserva (inclusión a través de caminos). - R7: la fecha de contratación de una excursión está entre la fecha de salida y la de regreso.
- R8: un viajero no aparece en dos reservas no canceladas de salidas con fechas solapadas.
- R9: estado ∈ {‘PENDIENTE’, ‘PAGADA’, ‘CANCELADA’}; descuento entre 0 y 50.
7.5. Decisiones discutibles
- Agregación frente a entidad. La agregación podría sustituirse por una entidad débil
PLAZA(oBILLETE) con una ocurrencia por viajero y reserva, relacionada N:M conEXCURSION. Es la forma habitual de implementarlo, y en el modelo relacional ambas soluciones dan las mismas tablas. La agregación es más fiel al enunciado. El módulo de agregación compara ambas en detalle. - Viajero y particular. Un viajero puede ser también un cliente particular (mismo DNI). Se podría generalizar en
PERSONAcon subtiposPARTICULARyVIAJERO(solapada), peroPARTICULARya es subtipo deCLIENTE, lo que daría una retícula sin raíz común. Se acepta la posible duplicación de datos personales y se documenta. - Salida débil. Si un viaje pudiera tener dos salidas el mismo día (dos grupos), el identificador parcial
fecha_salidano bastaría: habría que añadir un número de grupo o usar un identificador propio.
8. Caso 7: Taller mecánico
8.1. Enunciado
Una cadena de talleres mecánicos quiere gestionar las reparaciones.
La cadena tiene varios talleres (código, dirección, teléfono). Los empleados (código, DNI, nombre, fecha de alta, salario) trabajan en un taller; algunos son mecánicos (con especialidad: motor, electricidad, chapa…) y otros recepcionistas. Cada taller tiene un mecánico que es jefe de taller, y cada mecánico tiene un mecánico supervisor (excepto los jefes de taller).
De los clientes se guarda DNI o CIF, nombre, teléfono y email. Los vehículos (matrícula, número de bastidor, año, color, kilometraje actual) pertenecen a un cliente y son de un modelo (marca, nombre del modelo, motorización). Un vehículo puede cambiar de propietario y la cadena quiere saber qué vehículos ha tenido cada cliente.
Cuando un vehículo entra en un taller se abre una orden de reparación (número, fecha de entrada, kilometraje en la entrada, descripción de la avería, estado, fecha de salida), que registra un recepcionista. En cada orden pueden trabajar varios mecánicos, cada uno en varias intervenciones a lo largo de varios días (fecha, horas, tarea realizada). En cada orden se usan piezas (referencia, descripción, precio de venta, stock por taller) con una cantidad y el precio aplicado. Cada orden terminada genera, como mucho, una factura (número, fecha, importe total, forma de pago).
8.2. Análisis
| Elemento | Decisión | Justificación |
|---|---|---|
| EMPLEADO → MECANICO, RECEPCIONISTA | Jerarquía (parcial, exclusiva) | Puede haber otros empleados (administración); subtipos con relaciones propias |
| supervisa | Reflexiva 1:N sobre MECANICO | Cada mecánico tiene 0..1 supervisor |
| dirige | 1:1 MECANICO–TALLER | Jefe de taller |
| CLIENTE | Entidad, id nif | DNI o CIF en el mismo campo |
| VEHICULO, MODELO | Entidades | MODELO tiene datos propios (marca, motorización) |
| PROPIEDAD | Entidad débil de VEHICULO, id parcial fecha_desde | “Qué vehículos ha tenido cada cliente”: historia; un cliente puede recomprar un vehículo |
| ORDEN | Entidad | — |
| INTERVENCION | Entidad débil de ORDEN | Un mecánico interviene varias veces en la misma orden (repetición) |
| usa | N:M ORDEN–PIEZA con cantidad y precio_aplicado | Una vez por pieza y orden (las cantidades se suman) |
| stock por taller | N:M TALLER–PIEZA con stock | El stock depende del taller y de la pieza |
| FACTURA | Entidad; 1:1 opcional con ORDEN | — |
8.3. Diagrama
Parte 1: talleres y personal.
flowchart TB
TALLER[TALLER] --- ta_cod([<u>cod_taller</u>])
TALLER --- ta_dir([direccion])
TALLER --- ta_tel([telefono])
EMPLEADO[EMPLEADO] --- em_cod([<u>cod_empleado</u>])
EMPLEADO --- em_dni(["dni (AK)"])
EMPLEADO --- em_nom([nombre])
EMPLEADO --- em_fec([fecha_alta])
EMPLEADO --- em_sal([salario])
EMPLEADO ---|"(1,n)"| trabaja{"trabaja<br/>1:N"}
trabaja ---|"(1,1)"| TALLER
EMPLEADO --- je{{"es un<br/>(parcial, exclusiva)"}}
je --- MECANICO[MECANICO]
je --- RECEPCIONISTA[RECEPCIONISTA]
MECANICO --- me_esp([especialidad])
MECANICO ---|"(0,1) supervisor"| supervisa{"supervisa<br/>1:N"}
supervisa ---|"(0,n) supervisado"| MECANICO
MECANICO ---|"(1,1) jefe"| dirige{"dirige<br/>1:1"}
dirige ---|"(0,1)"| TALLER
TALLER ---|"(0,n)"| stock{"tiene_stock<br/>N:M"}
stock ---|"(0,n)"| PIEZA[PIEZA]
stock --- st_uni([unidades])
PIEZA --- pi_ref([<u>referencia</u>])
PIEZA --- pi_des([descripcion])
PIEZA --- pi_pre([precio_venta])
Parte 2: clientes y vehículos (con historia de propiedad).
flowchart TB
classDef debil stroke-width:4px
CLIENTE[CLIENTE] --- cl_nif([<u>nif</u>])
CLIENTE --- cl_nom([nombre])
CLIENTE --- cl_tel([telefono])
CLIENTE --- cl_ema([email])
VEHICULO[VEHICULO] --- ve_mat([<u>matricula</u>])
VEHICULO --- ve_bas(["num_bastidor (AK)"])
VEHICULO --- ve_anio([anio])
VEHICULO --- ve_col([color])
VEHICULO --- ve_km([km_actual])
VEHICULO ---|"(0,n)"| es_de{"es_de<br/>1:N"}
es_de ---|"(1,1)"| MODELO[MODELO]
MODELO --- mo_cod([<u>cod_modelo</u>])
MODELO --- mo_mar([marca])
MODELO --- mo_nom([nombre])
MODELO --- mo_mot([motorizacion])
VEHICULO ---|"(1,1)"| historia{"tiene<br/>ID"}:::debil
historia ---|"(1,n)"| PROPIEDAD[PROPIEDAD]:::debil
PROPIEDAD --- pr_des(["<u>fecha_desde</u> parcial"])
PROPIEDAD --- pr_has(["fecha_hasta (0,1)"])
PROPIEDAD ---|"(1,n)"| de_cli{"de<br/>1:N"}
de_cli ---|"(1,1)"| CLIENTE
Parte 3: órdenes de reparación.
flowchart TB
classDef debil stroke-width:4px
VEHICULO[VEHICULO] ---|"(1,1)"| entra{"entra<br/>1:N"}
entra ---|"(0,n)"| ORDEN[ORDEN]
ORDEN --- or_num([<u>num_orden</u>])
ORDEN --- or_fe([fecha_entrada])
ORDEN --- or_km([km_entrada])
ORDEN --- or_ave([averia])
ORDEN --- or_est([estado])
ORDEN --- or_fs(["fecha_salida (0,1)"])
TALLER[TALLER] ---|"(1,1)"| en{"en<br/>1:N"}
en ---|"(0,n)"| ORDEN
RECEPCIONISTA[RECEPCIONISTA] ---|"(1,1)"| registra{"registra<br/>1:N"}
registra ---|"(0,n)"| ORDEN
ORDEN ---|"(1,1)"| incluye{"incluye<br/>ID"}:::debil
incluye ---|"(0,n)"| INTERVENCION[INTERVENCION]:::debil
INTERVENCION --- in_num(["<u>num_intervencion</u> parcial"])
INTERVENCION --- in_fec([fecha])
INTERVENCION --- in_hor([horas])
INTERVENCION --- in_tar([tarea])
MECANICO[MECANICO] ---|"(1,1)"| realiza{"realiza<br/>1:N"}
realiza ---|"(0,n)"| INTERVENCION
ORDEN ---|"(0,n)"| usa{"usa<br/>N:M"}
usa ---|"(0,n)"| PIEZA[PIEZA]
usa --- us_can([cantidad])
usa --- us_pre([precio_aplicado])
ORDEN ---|"(1,1)"| genera{"genera<br/>1:1"}
genera ---|"(0,1)"| FACTURA[FACTURA]
FACTURA --- fa_num([<u>num_factura</u>])
FACTURA --- fa_fec([fecha])
FACTURA --- fa_imp([importe_total])
FACTURA --- fa_fp([forma_pago])
8.4. Restricciones
- R1: un mecánico no se supervisa a sí mismo y la cadena de supervisión no tiene ciclos.
- R2: el jefe de un taller trabaja en ese taller; el supervisor de un mecánico trabaja en el mismo taller.
- R3: un mecánico no tiene supervisor si y solo si es jefe de algún taller.
- R4: el recepcionista que registra una orden trabaja en el taller de la orden; los mecánicos que intervienen, también (salvo apoyos entre talleres, a confirmar).
- R5: los periodos de propiedad de un vehículo no se solapan; exactamente uno está vigente.
- R6: km_entrada de una orden ≥ km_entrada de las órdenes anteriores del mismo vehículo; km_actual del vehículo es el de su última orden (redundancia controlada).
- R7: solo se genera factura si el estado de la orden es ‘TERMINADA’; importe_total = suma de cantidad × precio_aplicado + horas × tarifa (derivación).
- R8: al usar piezas en una orden se descuenta el stock del taller de la orden; el stock no puede ser negativo.
8.5. Decisiones discutibles
- Propietario actual. Para saber el propietario actual hay que buscar la propiedad vigente. Si es una consulta muy frecuente, puede añadirse una relación 1:N VEHICULO–CLIENTE “propietario actual” como redundancia controlada.
- ¿Quién trae el coche? El cliente que trae el vehículo al taller no tiene por qué ser el propietario (una empresa de renting). Si interesa,
ORDENnecesita su propia relación conCLIENTE(y el ciclo no sería redundante). - Intervención como agregación. Una alternativa es una agregación de
ORDEN–MECANICOrelacionada con una entidadJORNADA. La entidad débil es más sencilla y admite repeticiones directamente. - Jefe de taller.
dirigese ha unido aMECANICO, porque el enunciado dice “un mecánico que es jefe de taller”. La (1,1) junto a MECANICO exige que todo taller tenga jefe.
9. Caso 8: Red social
9.1. Enunciado
Se quiere diseñar la base de datos de una red social profesional.
Los usuarios se registran con un nombre de usuario único, email, nombre completo, fecha de alta, biografía y foto de perfil (opcionales). Un usuario puede seguir a otros usuarios (no a sí mismo), con fecha de inicio; seguir no es recíproco. Un usuario también puede bloquear a otro: un usuario no puede seguir a otro que lo tiene bloqueado.
Los usuarios escriben publicaciones (fecha y hora, texto, visibilidad: pública o solo seguidores). Una publicación puede ser de texto, de imagen (una o varias URL de imagen con texto alternativo), de vídeo (URL, duración) o un artículo (título, cuerpo largo, tiempo estimado de lectura). Una publicación puede compartir otra publicación anterior.
Las publicaciones pueden recibir comentarios (fecha y hora, texto) de cualquier usuario, y un comentario puede responder a otro comentario. Tanto las publicaciones como los comentarios pueden recibir reacciones de los usuarios (me gusta, me encanta, interesante…); un usuario solo puede reaccionar una vez a cada publicación o comentario.
Las publicaciones pueden llevar etiquetas (hashtags) y mencionar a usuarios.
Existen grupos (nombre, descripción, privado o público) a los que pertenecen usuarios con un rol (administrador, moderador o miembro) y una fecha de ingreso. Una publicación puede publicarse en un grupo; en ese caso, su autor debe ser miembro del grupo.
9.2. Análisis
| Elemento | Decisión | Justificación |
|---|---|---|
| USUARIO | Entidad, id nombre_usuario; email (AK) | — |
| sigue, bloquea | Dos relaciones reflexivas N:M sobre USUARIO, con exclusión | No se puede seguir a quien te bloquea… (ver restricciones) |
| ELEMENTO | Supertipo de PUBLICACION y COMENTARIO (total, exclusiva) | Ambos tienen autor, fecha y texto y reciben reacciones: se generaliza para no duplicar reacciona y escribe |
| PUBLICACION → TEXTO, IMAGEN, VIDEO, ARTICULO | Segundo nivel de jerarquía (total, exclusiva) | Atributos propios; TEXTO no tiene nada propio, por eso la cobertura pasa a parcial sin dibujarlo |
| IMAGEN | Atributo multivaluado compuesto imagen (url, texto_alt) en el subtipo | “Una o varias URL” |
| comparte | Reflexiva 1:N sobre PUBLICACION | Cada publicación comparte 0..1 publicación |
| responde | Reflexiva 1:N sobre COMENTARIO | Hilo de respuestas |
| comenta | 1:N PUBLICACION–COMENTARIO | Cada comentario pertenece a una publicación |
| reacciona | N:M USUARIO–ELEMENTO con tipo | Una vez por par |
| ETIQUETA, menciona | N:M | — |
| GRUPO, miembro | N:M USUARIO–GRUPO con rol y fecha_ingreso | — |
| publicada_en | 1:N GRUPO–PUBLICACION opcional | Con restricción de inclusión a través de miembro |
9.3. Diagrama
Parte 1: usuarios, seguimiento, bloqueos y grupos.
flowchart TB
USUARIO[USUARIO] --- u_nom([<u>nombre_usuario</u>])
USUARIO --- u_ema(["email (AK)"])
USUARIO --- u_com([nombre_completo])
USUARIO --- u_fec([fecha_alta])
USUARIO --- u_bio(["biografia (0,1)"])
USUARIO --- u_fot(["foto_url (0,1)"])
USUARIO ---|"(0,n) seguidor"| sigue{"sigue<br/>N:M"}
sigue ---|"(0,n) seguido"| USUARIO
sigue --- si_fec([fecha_inicio])
USUARIO ---|"(0,n) bloqueador"| bloquea{"bloquea<br/>N:M"}
bloquea ---|"(0,n) bloqueado"| USUARIO
sigue -.-|"exclusión"| bloquea
USUARIO ---|"(0,n)"| miembro{"miembro<br/>N:M"}
miembro ---|"(0,n)"| GRUPO[GRUPO]
miembro --- mi_rol([rol])
miembro --- mi_fec([fecha_ingreso])
GRUPO --- g_id([<u>cod_grupo</u>])
GRUPO --- g_nom([nombre])
GRUPO --- g_des([descripcion])
GRUPO --- g_pri([privado])
Parte 2: jerarquía de elementos.
flowchart TB
ELEMENTO[ELEMENTO] --- el_id([<u>id_elemento</u>])
ELEMENTO --- el_fh([fecha_hora])
ELEMENTO --- el_txt([texto])
ELEMENTO --- j1{{"es un<br/>(total, exclusiva)"}}
j1 --- PUBLICACION[PUBLICACION]
j1 --- COMENTARIO[COMENTARIO]
PUBLICACION --- pu_vis([visibilidad])
PUBLICACION --- pu_tip([tipo])
PUBLICACION ---|"tipo"| j2{{"es un<br/>(parcial, exclusiva)"}}
j2 ---|"IMAGEN"| IMAGEN[PUB_IMAGEN]
j2 ---|"VIDEO"| VIDEO[PUB_VIDEO]
j2 ---|"ARTICULO"| ARTICULO[PUB_ARTICULO]
IMAGEN --- im_img(((imagen)))
im_img --- im_url([url])
im_img --- im_alt([texto_alt])
VIDEO --- vi_url([url])
VIDEO --- vi_dur([duracion_seg])
ARTICULO --- ar_tit([titulo])
ARTICULO --- ar_cue([cuerpo])
ARTICULO --- ar_lec([min_lectura]):::derivado
classDef derivado stroke-dasharray: 5 5
imagen es un atributo multivaluado compuesto (url + texto_alt). min_lectura es derivado del número de palabras del cuerpo.
Parte 3: relaciones de los elementos.
flowchart TB
USUARIO[USUARIO] ---|"(1,1) autor"| escribe{"escribe<br/>1:N"}
escribe ---|"(0,n)"| ELEMENTO[ELEMENTO]
USUARIO ---|"(0,n)"| reacciona{"reacciona<br/>N:M"}
reacciona ---|"(0,n)"| ELEMENTO
reacciona --- re_tip([tipo])
PUBLICACION[PUBLICACION] ---|"(0,1) original"| comparte{"comparte<br/>1:N"}
comparte ---|"(0,n) republicacion"| PUBLICACION
PUBLICACION ---|"(1,1)"| comenta{"comenta<br/>1:N"}
comenta ---|"(0,n)"| COMENTARIO[COMENTARIO]
COMENTARIO ---|"(0,1) padre"| responde{"responde<br/>1:N"}
responde ---|"(0,n) respuesta"| COMENTARIO
PUBLICACION ---|"(0,n)"| etiqueta{"etiquetada<br/>N:M"}
etiqueta ---|"(0,n)"| ETIQUETA[ETIQUETA]
ETIQUETA --- et_nom([<u>texto</u>])
PUBLICACION ---|"(0,n)"| menciona{"menciona<br/>N:M"}
menciona ---|"(0,n)"| USUARIO
GRUPO[GRUPO] ---|"(0,1)"| publicada{"publicada_en<br/>1:N"}
publicada ---|"(0,n)"| PUBLICACION
Lectura de las reflexivas:
comparte: una publicación (en su papel de republicación) comparte 0..1 publicación original (pareja junto al papel original); una publicación original puede ser compartida por 0..n republicaciones.responde: un comentario responde a 0..1 comentario padre; un comentario puede tener 0..n respuestas.
9.4. Restricciones
- R1:
sigueybloqueason irreflexivas (nadie se sigue ni se bloquea a sí mismo). - R2: si A bloquea a B, B no puede seguir a A. Ojo: la exclusión dibujada prohíbe el mismo par ordenado en las dos relaciones (A sigue a B y A bloquea a B). La regla del enunciado es sobre el par invertido, así que hay que escribirla como restricción explícita; ambas reglas suelen aplicarse a la vez (al bloquear a alguien, se deja de seguirlo).
- R3: un comentario que responde a otro pertenece a la misma publicación que su padre.
- R4: una publicación compartida es anterior a la que la comparte; no hay ciclos de compartición.
- R5: si una publicación está en un grupo, su autor es miembro del grupo (inclusión a través de caminos: (autor, grupo) ∈
miembro). - R6: cada grupo tiene al menos un administrador.
- R7: un usuario no puede comentar ni reaccionar a publicaciones de alguien que lo tiene bloqueado.
- R8: las publicaciones con visibilidad ‘SEGUIDORES’ solo las ven los seguidores del autor (regla de acceso: aplicación).
- R9: tipo de reacción ∈ {‘ME_GUSTA’, ‘ME_ENCANTA’, ‘INTERESANTE’, ‘APOYO’}; rol ∈ {‘ADMIN’, ‘MODERADOR’, ‘MIEMBRO’}.
9.5. Decisiones discutibles
- Generalizar publicación y comentario. Es la decisión clave del caso. Sin el supertipo
ELEMENTO, habría que duplicarescribeyreacciona(una para publicaciones y otra para comentarios), o usar una exclusividad entre dos relacionesreacciona_pubyreacciona_com. La generalización simplifica el diagrama y las consultas (“todas las reacciones de un usuario”). - Publicación de texto. No se dibuja el subtipo TEXTO porque no tiene nada propio: por eso la segunda especialización es parcial, aunque en la realidad todas las publicaciones tengan un tipo. El discriminante
tipoincluye el valor ‘TEXTO’. - Mensajes privados. El enunciado no los pide. Si se añadieran, serían una entidad
MENSAJEcon dos relaciones conUSUARIO(emisor y receptor), o una entidadCONVERSACIONcon participantes N:M para chats de grupo. - Contadores. Las redes sociales almacenan contadores (número de seguidores, de reacciones) como redundancia controlada por rendimiento. En el modelo conceptual son atributos derivados.
10. Caso 9: el esquema EMPRESA del curso
Este último caso funciona al revés que los anteriores: partimos de las tablas que ya existen (el script empresa_crear.sql que usas en todo el bloque de SQL) y deducimos el modelo E/R del que proceden. Es lo que se llama ingeniería inversa, y es una tarea muy habitual en la vida profesional: heredar una base de datos sin documentación y tener que entenderla.
10.1. Enunciado reconstruido
A partir del script, el enunciado que daría lugar a este esquema sería el siguiente:
Una empresa que vende productos informáticos quiere gestionar su organización y sus ventas.
La empresa se organiza en departamentos (código, nombre único, localidad y presupuesto, que puede no conocerse). Los empleados (código, nombre, apellidos, email único, teléfono opcional, fecha de alta, oficio, salario y comisión opcional) pertenecen, en general, a un departamento, aunque puede haber empleados sin departamento asignado. Cada empleado puede tener un jefe, que es otro empleado.
Los clientes (código, nombre, NIF único, email, teléfono, ciudad, provincia, fecha de alta y límite de crédito) pueden tener asignado un empleado como comercial.
Los productos (código, nombre, descripción, precio, stock, fecha de alta y si está descatalogado) pertenecen a una categoría (código, nombre único). Las categorías se organizan en jerarquía: cada categoría puede tener una categoría padre.
Los clientes hacen pedidos (número, fecha, fecha de envío, estado), que puede gestionar un empleado. Cada pedido tiene varias líneas, numeradas dentro del pedido; cada línea es de un producto, con una cantidad, el precio unitario aplicado y un descuento.
Los departamentos llevan proyectos (código, nombre único, fechas de inicio y fin, presupuesto). Los empleados se asignan a proyectos; de cada asignación se guarda la fecha, las horas dedicadas y la función del empleado en el proyecto.
10.2. De las tablas a los elementos del E/R
El razonamiento de la ingeniería inversa se basa en reconocer los patrones que deja cada construcción del E/R al transformarse en tablas (el proceso directo lo estudiarás en el módulo 17):
| Patrón en el script | Elemento E/R deducido |
|---|---|
Tabla con PK propia de una columna (departamentos, empleados…) | Entidad fuerte |
| FK hacia otra tabla en una columna que admite nulos | Relación 1:N con participación opcional del lado N |
FK con NOT NULL | Relación 1:N con participación obligatoria |
FK de una tabla hacia sí misma (jefe_id, categoria_padre_id) | Relación reflexiva |
PK compuesta que incluye una FK con ON DELETE CASCADE (lineas_pedido) | Entidad débil |
PK compuesta formada por dos FK, con columnas adicionales (asignaciones) | Relación N:M con atributos |
UNIQUE | Clave alternativa |
CHECK (... IN (...)) | Dominio de un atributo (restricción) |
CHECK entre dos columnas (fecha_envio >= fecha_pedido) | Restricción entre atributos |
Aplicándolo:
| Tabla / columna | Deducción |
|---|---|
departamentos | Entidad DEPARTAMENTO; nombre (AK); presupuesto opcional |
empleados.dept_id (nulable) | Relación pertenece EMPLEADO–DEPARTAMENTO; un empleado tiene 0..1 departamento |
empleados.jefe_id (nulable) | Reflexiva dirige: cada empleado tiene 0..1 jefe |
clientes.comercial_id (nulable) | Relación atiende EMPLEADO–CLIENTE; cada cliente tiene 0..1 comercial |
categorias.categoria_padre_id (nulable) | Reflexiva contiene sobre CATEGORIA |
productos.categoria_id (NOT NULL) | Relación clasifica: cada producto, exactamente 1 categoría |
pedidos.cliente_id (NOT NULL) | Relación realiza: cada pedido, exactamente 1 cliente |
pedidos.empleado_id (nulable) | Relación gestiona: cada pedido, 0..1 empleado |
lineas_pedido PK (pedido_id, num_linea), ON DELETE CASCADE | Entidad débil LINEA_PEDIDO de PEDIDO, identificador parcial num_linea |
lineas_pedido.producto_id (NOT NULL) | Relación de: cada línea, exactamente 1 producto |
proyectos.dept_id (nulable) | Relación lleva: cada proyecto, 0..1 departamento |
asignaciones PK (emp_id, proyecto_id) + fecha_asignacion, horas, funcion | Relación N:M asignado EMPLEADO–PROYECTO con atributos |
10.3. Cardinalidades mínimas que el script no dice
El script solo informa de las cardinalidades del lado de la clave ajena (si la FK admite nulos, la mínima es 0; si es NOT NULL, es 1). Las mínimas del otro lado (¿todo departamento tiene empleados?, ¿todo pedido tiene líneas?) no se pueden expresar con una FK, así que las deducimos de los datos de empresa_datos.sql y del sentido común:
| Pregunta | Respuesta en los datos | Mínima |
|---|---|---|
| ¿Todo departamento tiene empleados? | No: el departamento 70 (INVESTIGACIÓN) no tiene | 0 |
| ¿Todo empleado tiene departamento? | No: el empleado 160 no tiene | 0 (lo dice la FK nulable) |
| ¿Todo cliente tiene pedidos? | No: los clientes 1013 y 1014 no tienen | 0 |
| ¿Todo cliente tiene comercial? | No: 1009 y 1013 no tienen | 0 (FK nulable) |
| ¿Todo pedido lo gestiona un empleado? | No: los pedidos 5014 y 5023 no tienen empleado | 0 (FK nulable) |
| ¿Todo producto se ha vendido? | No: el producto 14 (descatalogado) nunca se ha vendido | 0 |
| ¿Todo proyecto tiene asignaciones? | No: el proyecto 5 no tiene | 0 |
| ¿Todo pedido tiene líneas? | En los datos, sí: los 24 pedidos tienen líneas. Conceptualmente, un pedido sin líneas no tiene sentido | 1 (restricción que el script no garantiza) |
| ¿Toda categoría tiene productos? | Las categorías “padre” (Informática, Electrónica, Oficina) pueden no tener productos directamente | 0 |
10.4. Diagrama
Parte 1: organización (departamentos, empleados, proyectos).
flowchart TB
DEPARTAMENTO[DEPARTAMENTO] --- d_id([<u>dept_id</u>])
DEPARTAMENTO --- d_nom(["nombre (AK)"])
DEPARTAMENTO --- d_loc(["localidad (0,1)"])
DEPARTAMENTO --- d_pre(["presupuesto (0,1)"])
EMPLEADO[EMPLEADO] --- e_id([<u>emp_id</u>])
EMPLEADO --- e_nom([nombre])
EMPLEADO --- e_ape([apellidos])
EMPLEADO --- e_ema(["email (AK)"])
EMPLEADO --- e_tel(["telefono (0,1)"])
EMPLEADO --- e_fec([fecha_alta])
EMPLEADO --- e_ofi([oficio])
EMPLEADO --- e_sal([salario])
EMPLEADO --- e_com(["comision (0,1)"])
EMPLEADO ---|"(0,n)"| pertenece{"pertenece<br/>1:N"}
pertenece ---|"(0,1)"| DEPARTAMENTO
EMPLEADO ---|"(0,1) jefe"| dirige{"dirige<br/>1:N"}
dirige ---|"(0,n) subordinado"| EMPLEADO
PROYECTO[PROYECTO] --- p_id([<u>proyecto_id</u>])
PROYECTO --- p_nom(["nombre (AK)"])
PROYECTO --- p_ini([fecha_inicio])
PROYECTO --- p_fin(["fecha_fin (0,1)"])
PROYECTO --- p_pre(["presupuesto (0,1)"])
DEPARTAMENTO ---|"(0,1)"| lleva{"lleva<br/>1:N"}
lleva ---|"(0,n)"| PROYECTO
EMPLEADO ---|"(0,n)"| asignado{"asignado<br/>N:M"}
asignado ---|"(0,n)"| PROYECTO
asignado --- a_fec([fecha_asignacion])
asignado --- a_hor(["horas (0,1)"])
asignado --- a_fun(["funcion (0,1)"])
Parte 2: ventas (clientes, pedidos, productos, categorías).
flowchart TB
classDef debil stroke-width:4px
classDef derivado stroke-dasharray: 5 5
CLIENTE[CLIENTE] --- c_id([<u>cliente_id</u>])
CLIENTE --- c_nom([nombre])
CLIENTE --- c_nif(["nif (AK)"])
CLIENTE --- c_ema(["email (0,1)"])
CLIENTE --- c_tel(["telefono (0,1)"])
CLIENTE --- c_ciu(["ciudad (0,1)"])
CLIENTE --- c_pro(["provincia (0,1)"])
CLIENTE --- c_fec([fecha_alta])
CLIENTE --- c_lim(["limite_credito (0,1)"])
EMPLEADO[EMPLEADO] ---|"(0,1) comercial"| atiende{"atiende<br/>1:N"}
atiende ---|"(0,n)"| CLIENTE
CLIENTE ---|"(1,1)"| realiza{"realiza<br/>1:N"}
realiza ---|"(0,n)"| PEDIDO[PEDIDO]
EMPLEADO ---|"(0,1)"| gestiona{"gestiona<br/>1:N"}
gestiona ---|"(0,n)"| PEDIDO
PEDIDO --- pe_id([<u>pedido_id</u>])
PEDIDO --- pe_fec([fecha_pedido])
PEDIDO --- pe_env(["fecha_envio (0,1)"])
PEDIDO --- pe_est([estado])
PEDIDO --- pe_tot([importe_total]):::derivado
PEDIDO ---|"(1,1)"| contiene{"contiene<br/>ID"}:::debil
contiene ---|"(1,n)"| LINEA[LINEA_PEDIDO]:::debil
LINEA --- l_num(["<u>num_linea</u> parcial"])
LINEA --- l_can([cantidad])
LINEA --- l_pre([precio_unitario])
LINEA --- l_des([descuento])
LINEA --- l_imp([importe]):::derivado
LINEA ---|"(0,n)"| de{"de<br/>1:N"}
de ---|"(1,1)"| PRODUCTO[PRODUCTO]
PRODUCTO --- pr_id([<u>producto_id</u>])
PRODUCTO --- pr_nom([nombre])
PRODUCTO --- pr_des(["descripcion (0,1)"])
PRODUCTO --- pr_pre([precio])
PRODUCTO --- pr_sto([stock])
PRODUCTO --- pr_fec(["fecha_alta (0,1)"])
PRODUCTO --- pr_dca([descatalogado])
PRODUCTO ---|"(0,n)"| clasifica{"clasifica<br/>1:N"}
clasifica ---|"(1,1)"| CATEGORIA[CATEGORIA]
CATEGORIA --- ca_id([<u>categoria_id</u>])
CATEGORIA --- ca_nom(["nombre (AK)"])
CATEGORIA ---|"(0,1) padre"| subcat{"contiene<br/>1:N"}
subcat ---|"(0,n) hija"| CATEGORIA
Los atributos derivados importe (de la línea) e importe_total (del pedido) no existen como columnas en el script: se calculan en las consultas. Se dibujan porque son datos importantes para el usuario y así queda documentada su regla de derivación.
Las parejas más interesantes, leídas con la convención del curso:
pertenece: (0,1) junto a DEPARTAMENTO → un empleado pertenece a 0 o 1 departamentos (empleado 160); (0,n) junto a EMPLEADO → un departamento tiene de 0 a n empleados (departamento 70).dirige: (0,1) junto al papel jefe → cada empleado tiene 0 o 1 jefe (el 100, director general, no tiene); (0,n) junto a subordinado → un empleado dirige de 0 a n empleados.contiene(pedido-líneas): (1,1) junto a PEDIDO → cada línea es de exactamente un pedido; (1,n) junto a LINEA_PEDIDO → cada pedido tiene al menos una línea.asignado: N:M con mínimas 0 en ambos lados (proyecto 5 sin asignaciones; empleados sin proyectos).
10.5. Paso a relacional (anticipo)
Para cerrar el círculo, este es el esquema relacional que corresponde al diagrama, y que es exactamente el del script (las reglas de transformación se estudian en el módulo 17):
DEPARTAMENTOS (dept_id, nombre, localidad (N), presupuesto (N))
- UK: nombre
EMPLEADOS (emp_id, nombre, apellidos, email, telefono (N), fecha_alta, oficio, salario, comision (N), jefe_id (N), dept_id (N))
- jefe_id → EMPLEADOS(emp_id); dept_id → DEPARTAMENTOS(dept_id)
- UK: email
CLIENTES (cliente_id, nombre, nif, email (N), telefono (N), ciudad (N), provincia (N), fecha_alta, limite_credito (N), comercial_id (N))
- comercial_id → EMPLEADOS(emp_id)
- UK: nif
CATEGORIAS (categoria_id, nombre, categoria_padre_id (N))
- categoria_padre_id → CATEGORIAS(categoria_id)
- UK: nombre
PRODUCTOS (producto_id, nombre, descripcion (N), categoria_id, precio, stock, fecha_alta (N), descatalogado)
- categoria_id → CATEGORIAS(categoria_id)
PEDIDOS (pedido_id, cliente_id, empleado_id (N), fecha_pedido, fecha_envio (N), estado)
- cliente_id → CLIENTES(cliente_id); empleado_id → EMPLEADOS(emp_id)
LINEAS_PEDIDO (*pedido_id*, num_linea, producto_id, cantidad, precio_unitario, descuento)
- pedido_id → PEDIDOS(pedido_id) [ON DELETE CASCADE]; producto_id → PRODUCTOS(producto_id)
PROYECTOS (proyecto_id, nombre, dept_id (N), fecha_inicio, fecha_fin (N), presupuesto (N))
- dept_id → DEPARTAMENTOS(dept_id)
- UK: nombre
ASIGNACIONES (*emp_id*, *proyecto_id*, fecha_asignacion, horas (N), funcion (N))
- emp_id → EMPLEADOS(emp_id); proyecto_id → PROYECTOS(proyecto_id) [ON DELETE CASCADE]
Los atributos marcados con (N) admiten nulos; el resto son obligatorios.
Y su grafo relacional:
flowchart LR
LINEAS_PEDIDO --> PEDIDOS
LINEAS_PEDIDO --> PRODUCTOS
PEDIDOS --> CLIENTES
PEDIDOS --> EMPLEADOS
CLIENTES --> EMPLEADOS
EMPLEADOS --> EMPLEADOS
EMPLEADOS --> DEPARTAMENTOS
PRODUCTOS --> CATEGORIAS
CATEGORIAS --> CATEGORIAS
PROYECTOS --> DEPARTAMENTOS
ASIGNACIONES --> EMPLEADOS
ASIGNACIONES --> PROYECTOS
10.6. Restricciones
Las que el script sí implementa (con CHECK, UNIQUE y NOT NULL):
- R1: presupuesto de departamentos y proyectos ≥ 0; salario > 0; comisión ≥ 0; límite de crédito ≥ 0; precio ≥ 0; stock ≥ 0; cantidad > 0; precio_unitario ≥ 0; horas ≥ 0.
- R2: descuento entre 0 y 100.
- R3: descatalogado ∈ {‘S’, ‘N’}; estado del pedido ∈ {‘PENDIENTE’, ‘ENVIADO’, ‘ENTREGADO’, ‘CANCELADO’}.
- R4: fecha_envio ≥ fecha_pedido; fecha_fin ≥ fecha_inicio en los proyectos.
- R5: son únicos el nombre de departamento, el email de empleado, el NIF de cliente, el nombre de categoría y el nombre de proyecto.
Las que el script no implementa y deberían documentarse:
- R6: todo pedido tiene al menos una línea (mínima 1 junto a LINEA_PEDIDO).
- R7: un empleado no es jefe de sí mismo y la jerarquía de jefes no tiene ciclos; lo mismo para la jerarquía de categorías.
- R8: el comercial de un cliente tiene oficio ‘VENDEDOR’ (o pertenece al departamento de VENTAS): regla del negocio a confirmar.
- R9: solo los vendedores tienen comisión.
- R10: un pedido ‘ENVIADO’ o ‘ENTREGADO’ tiene fecha_envio; uno ‘PENDIENTE’ no la tiene.
- R11: no se venden productos descatalogados en pedidos posteriores a su descatalogación.
- R12: la fecha de asignación de un empleado a un proyecto está entre las fechas de inicio y fin del proyecto.
10.7. Decisiones discutibles
- ¿Jerarquía de empleados por oficio? Podría modelarse
EMPLEADO→VENDEDOR,DIRECTOR… conoficiocomo discriminante. No se ha hecho porque, según la regla de oro, solo el vendedor tendría algo propio (comisión, clientes que atiende) y el diseño del script usa un atributo. Una alternativa intermedia esEMPLEADO→COMERCIAL(parcial) con el atributocomisiony la relaciónatiende, lo que convertiría R8 y R9 en restricciones estructurales. - ¿Líneas de pedido como entidad débil o como N:M? Podría modelarse PEDIDO–PRODUCTO como relación N:M con atributos (cantidad, precio_unitario, descuento). El script, sin embargo, usa
num_lineaen la clave, lo que permite que un mismo producto aparezca en dos líneas del mismo pedido (por ejemplo, con descuentos distintos). Con una N:M eso no sería posible. Por eso la entidad débil es la interpretación fiel del script. precio_unitariono es redundante conproductos.precio: es el precio en el momento de la venta (dato congelado, módulo 13).- Comercial del cliente y empleado del pedido no son redundantes: el comercial asignado puede cambiar y el pedido conserva quién lo gestionó (módulo 13, sección 6.5).
localidadyciudadcomo atributos. Si se quisiera normalizar las poblaciones, aparecería una entidadLOCALIDAD. El esquema mantiene texto libre por simplicidad.- Asignaciones sin repetición. La PK (emp_id, proyecto_id) impide asignar dos veces al mismo empleado al mismo proyecto. Si un empleado pudiera salir de un proyecto y volver a entrar, haría falta reificar la asignación (módulo 13, sección 8).
11. Ejercicios
Los dos primeros ejercicios son modificaciones de los casos anteriores y están resueltos. Los seis restantes son supuestos completos propuestos para que los resuelvas siguiendo la metodología del módulo 14.
Ejercicio 1: Ampliar la biblioteca con reservas
Amplía el caso 1 con estos requisitos: un socio puede reservar un libro (no un ejemplar concreto) cuando todos sus ejemplares están prestados; de cada reserva se guarda la fecha y si ha sido atendida. Cuando un ejemplar se devuelve, se avisa al socio con la reserva más antigua. Un socio no puede tener dos reservas pendientes del mismo libro, pero sí reservar el mismo libro en otro momento.
Ver solución
La reserva es de un libro, no de un ejemplar, y puede repetirse en el tiempo: es una entidad.
flowchart TB
SOCIO[SOCIO] ---|"(1,1)"| hace{"hace<br/>1:N"}
hace ---|"(0,n)"| RESERVA[RESERVA]
RESERVA --- r_num([<u>num_reserva</u>])
RESERVA --- r_fec([fecha_reserva])
RESERVA --- r_ate([atendida])
RESERVA ---|"(0,n)"| de{"de<br/>1:N"}
de ---|"(1,1)"| LIBRO[LIBRO]
RESERVA ---|"(0,1)"| atendida_con{"atendida_con<br/>1:1"}
atendida_con ---|"(0,1)"| PRESTAMO[PRESTAMO]
Se añade una relación opcional 1:1 atendida_con entre la reserva y el préstamo que la satisface: así se sabe qué préstamo atendió cada reserva y atendida pasa a ser derivado (verdadero si la reserva participa en atendida_con).
Restricciones nuevas:
- R8: solo se puede reservar un libro si ninguno de sus ejemplares está disponible (todos prestados sin devolver o perdidos).
- R9: un socio no tiene dos reservas no atendidas del mismo libro.
- R10: el préstamo que atiende una reserva es del mismo socio y de un ejemplar del libro reservado (caminos coincidentes).
- R11: un socio no puede reservar un libro del que tiene un ejemplar prestado sin devolver.
Ejercicio 2: Cambio de requisito en EMPRESA
La dirección de la empresa del caso 9 quiere: (a) conservar la historia de los departamentos por los que ha pasado cada empleado; (b) poder asignar varias veces al mismo empleado al mismo proyecto en periodos distintos. Modifica el diagrama y explica qué cambiaría en las tablas.
Ver solución
Las dos peticiones son casos de la dimensión temporal y de relaciones con repetición (módulo 13).
flowchart TB
classDef debil stroke-width:4px
EMPLEADO[EMPLEADO] ---|"(0,n)"| pertenece{"pertenece<br/>1:N"}
pertenece ---|"(0,1)"| DEPARTAMENTO[DEPARTAMENTO]
EMPLEADO ---|"(1,1)"| tiene{"tiene<br/>ID"}:::debil
tiene ---|"(0,n)"| ESTANCIA[ESTANCIA_DEPT]:::debil
ESTANCIA --- es_ini(["<u>fecha_inicio</u> parcial"])
ESTANCIA --- es_fin(["fecha_fin (0,1)"])
ESTANCIA ---|"(0,n)"| en{"en<br/>1:N"}
en ---|"(1,1)"| DEPARTAMENTO
EMPLEADO ---|"(1,1)"| participa{"participa<br/>1:N"}
participa ---|"(0,n)"| ASIGNACION[ASIGNACION]
ASIGNACION --- as_id([<u>asignacion_id</u>])
ASIGNACION --- as_ini([fecha_asignacion])
ASIGNACION --- as_fin(["fecha_fin (0,1)"])
ASIGNACION --- as_hor(["horas (0,1)"])
ASIGNACION --- as_fun(["funcion (0,1)"])
ASIGNACION ---|"(0,n)"| a_proy{"a<br/>1:N"}
a_proy ---|"(1,1)"| PROYECTO[PROYECTO]
- (a) Se mantiene
pertenece(departamento actual) y se añade la entidad débilESTANCIA_DEPTcon la historia. Restricción: la estancia vigente coincide con el departamento actual; las estancias no se solapan. - (b) La relación N:M
asignadose reifica en la entidadASIGNACIONcon identificador propio. Restricción: las asignaciones del mismo empleado al mismo proyecto no se solapan en el tiempo.
En las tablas: aparecería una tabla nueva ESTANCIAS_DEPT con PK (emp_id, fecha_inicio), y la tabla ASIGNACIONES cambiaría su PK (emp_id, proyecto_id) por una PK propia asignacion_id, quedando emp_id y proyecto_id como claves ajenas NOT NULL sin unicidad conjunta.
Ejercicio 3 (propuesto): Cadena hotelera
Una cadena hotelera tiene hoteles (código, nombre, categoría, dirección, ciudad) con habitaciones numeradas dentro de cada hotel (tipo, capacidad, precio base por temporada: alta, media o baja). Los clientes (particulares o agencias de viajes con un porcentaje de comisión) hacen reservas de una o varias habitaciones para un intervalo de fechas, con un régimen (solo alojamiento, desayuno, media pensión, pensión completa). Durante la estancia, los huéspedes consumen servicios (spa, restaurante, lavandería) que se cargan a la habitación con fecha, importe y empleado que lo registra. Los empleados pertenecen a un hotel y tienen un puesto; cada hotel tiene un director. Al final de la estancia se emite una factura que puede pagarse en varios pagos. Dibuja el diagrama completo, con sus restricciones y decisiones discutibles (en particular: ¿la reserva es de una habitación concreta o de un tipo de habitación?).
Ejercicio 4 (propuesto): Aerolínea
Una aerolínea quiere gestionar su operación. Tiene aviones (matrícula, fecha de fabricación) de distintos modelos (fabricante, nombre, número de asientos por clase). Opera rutas entre aeropuertos (código IATA, nombre, ciudad, país); cada ruta tiene un aeropuerto de origen y uno de destino, distintos. Cada ruta se cubre con vuelos programados (número de vuelo, días de la semana, hora de salida), y cada vuelo programado da lugar a vuelos concretos en fechas determinadas, con el avión asignado, la tripulación (pilotos y auxiliares, con su papel: comandante, primer oficial, sobrecargo…) y el estado (programado, retrasado, cancelado…). Los pasajeros compran billetes para uno o varios vuelos concretos (con escalas), con asiento y clase. Los pilotos están habilitados para pilotar ciertos modelos de avión desde una fecha. Identifica las relaciones reflexivas o con papeles, las débiles, las inclusiones (¿qué pasa si se asigna a un vuelo un piloto no habilitado para el modelo del avión?) y la dimensión temporal.
Ejercicio 5 (propuesto): Inmobiliaria
Una red de oficinas inmobiliarias gestiona inmuebles (referencia, dirección, superficie, año de construcción) que pueden ser pisos (planta, ascensor), casas (superficie de parcela, plantas) o locales comerciales (escaparate, licencia de actividad). Cada inmueble tiene uno o varios propietarios (personas o empresas, con su porcentaje de propiedad) y puede ofrecerse en venta, en alquiler o en ambas modalidades, con un precio para cada una y un historial de cambios de precio. Los agentes de cada oficina organizan visitas de clientes interesados a los inmuebles (fecha, hora, valoración del cliente); un cliente puede visitar varias veces el mismo inmueble. Las operaciones cerradas (ventas y contratos de alquiler) registran el comprador o inquilino, el agente que la cerró, el precio final y la comisión. Atención a las trampas de conexión: ¿qué oficina ofrece un inmueble si no tiene agente asignado?
Ejercicio 6 (propuesto): Gestión de congresos científicos
Una asociación organiza congresos anuales (edición, ciudad, fechas). Los investigadores (ORCID, nombre, email, afiliación a una institución) envían artículos (título, resumen, palabras clave) a un congreso; un artículo tiene uno o varios autores, con un orden, y uno de ellos es el autor de contacto. Cada artículo es revisado por tres revisores del comité de programa del congreso, que emiten una puntuación y un informe; un revisor no puede revisar artículos propios ni de investigadores de su misma institución. Los artículos aceptados se presentan en sesiones (sala, fecha, hora, moderador) del congreso. Los asistentes se inscriben al congreso con una cuota que depende de su tipo (estudiante, socio, no socio). Identifica las restricciones de exclusión, de inclusión y de caminos, y decide si el comité de programa es una relación o una entidad.
Ejercicio 7 (propuesto): Banco
Un banco tiene sucursales (código, dirección) y clientes (personas físicas o jurídicas). Los clientes son titulares de cuentas (IBAN, fecha de apertura, saldo), que pueden ser corrientes (con límite de descubierto) o de ahorro (con tipo de interés); una cuenta puede tener varios titulares, con un tipo de titularidad (titular, cotitular, autorizado) y un orden. Cada cuenta se abre en una sucursal. Sobre las cuentas se realizan movimientos (fecha y hora, importe, concepto, tipo: ingreso, reintegro, transferencia, recibo); una transferencia entre dos cuentas del banco genera dos movimientos relacionados. Los clientes pueden tener tarjetas (número, tipo débito o crédito, fecha de caducidad) asociadas a una cuenta y a un titular de esa cuenta, y préstamos con un plan de cuotas (número de cuota, fecha de vencimiento, importe, pagada). Modela además los gestores personales: cada cliente con más de 100 000 € en cuentas tiene un gestor asignado, que es empleado de una sucursal.
Ejercicio 8 (propuesto): Elecciones municipales
Un ayuntamiento quiere gestionar unas elecciones. El municipio se divide en distritos, cada distrito en secciones y cada sección en mesas electorales (letra A, B…); cada mesa tiene un presidente y dos vocales, que son electores del censo de esa sección, y suplentes. Los electores (DNI, nombre, fecha de nacimiento, dirección) están censados en una sección y votan en una mesa según la primera letra de su apellido. Las candidaturas (partidos, coaliciones o agrupaciones de electores, con siglas y logotipo) presentan una lista ordenada de candidatos, que deben ser electores del municipio, en cada elección. Se registran los resultados de cada candidatura en cada mesa (votos), además de los votos nulos y en blanco de cada mesa, y los interventores y apoderados que cada candidatura acredita en cada mesa. El sistema debe conservar los datos de varias elecciones (municipales de 2023, de 2027…). Presta especial atención a la dimensión temporal (un elector puede cambiar de sección entre dos elecciones) y a las coaliciones (que están formadas por varios partidos).
Resumen
| Caso | Entidades principales | Construcciones clave | Restricción más interesante |
|---|---|---|---|
| Biblioteca | LIBRO, EJEMPLAR, SOCIO, PRESTAMO | Débil (ejemplar), reificación (préstamo), multivaluado | Un ejemplar no está en dos préstamos abiertos |
| Centro de FP | CICLO, MODULO, GRUPO, PROFESOR, MATRICULA | Ternaria (imparte), 1:1 (jefe), débil (calificación) | El módulo matriculado pertenece al ciclo del grupo |
| Clínica | PERSONAL, PACIENTE, CITA, CONSULTA, INGRESO | Jerarquía, débil 1:1 (consulta), débil (habitación) | Ingresos simultáneos ≤ camas de la habitación |
| Liga | EQUIPO, JUGADOR, TEMPORADA, PARTIDO | Ternaria (ficha), papeles con exclusión, (3,3) | El jugador tiene ficha con uno de los dos equipos del partido |
| Streaming | CUENTA, PERFIL, CONTENIDO, EPISODIO | Jerarquía, débil de débil, exclusividad | Perfiles ≤ máximo del plan vigente |
| Agencia de viajes | CLIENTE, VIAJE, SALIDA, RESERVA, VIAJERO | Generalización, débiles, agregación | La excursión contratada la ofrece el viaje de la salida |
| Taller | EMPLEADO, VEHICULO, ORDEN, INTERVENCION | Reflexiva, historia de propiedad, 1:1 opcional | Sin supervisor si y solo si es jefe de taller |
| Red social | USUARIO, ELEMENTO, PUBLICACION, COMENTARIO | Varias reflexivas, jerarquía de dos niveles, exclusión | Una respuesta pertenece a la publicación de su comentario padre |
| EMPRESA | DEPARTAMENTO, EMPLEADO, CLIENTE, PEDIDO, PRODUCTO | Ingeniería inversa, reflexivas, débil, N:M con atributos | Todo pedido tiene al menos una línea (no la garantiza el script) |