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:

  1. Enunciado, redactado como en un examen.
  2. Análisis: entidades, atributos y relaciones identificados, con su justificación.
  3. 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).
  4. Restricciones que no se pueden expresar en el diagrama.
  5. 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_hasta posterior 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 con fecha_prestamo como 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 SANCION relacionada con PRESTAMO. Además, sancionado_hasta es 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 SEDE relacionada 1:N con EJEMPLAR. 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 orden en la relación escribe.

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_ACADEMICO relacionada con GRUPO.
  • 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 con MODULO, 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 imparte por 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 entidad DOCENCIA (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, CAMA sería débil de HABITACION y el ingreso se relacionaría con la cama.
  • Consulta y cita en una sola entidad. Muchos sistemas tienen una única entidad CITA con 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_ASISTENCIAL que 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, local y visitante).
  • 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 (principal 1: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_local y puntos_visitante serí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_perfiles del 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_pelicula y de_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_REPRODUCIBLE con subtipos PELICULA y EPISODIO (con duración común) y relacionar REPRODUCCION solo con el supertipo. Pero PELICULA ya es subtipo de CONTENIDO: 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 con PAPEL (o, equivalentemente, un atributo papel que 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 viaja con 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_regreso es 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 ofrece el 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 (o BILLETE) con una ocurrencia por viajero y reserva, relacionada N:M con EXCURSION. 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 PERSONA con subtipos PARTICULAR y VIAJERO (solapada), pero PARTICULAR ya es subtipo de CLIENTE, 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_salida no 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, ORDEN necesita su propia relación con CLIENTE (y el ciclo no sería redundante).
  • Intervención como agregación. Una alternativa es una agregación de ORDEN–MECANICO relacionada con una entidad JORNADA. La entidad débil es más sencilla y admite repeticiones directamente.
  • Jefe de taller. dirige se ha unido a MECANICO, 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: sigue y bloquea son 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 duplicar escribe y reacciona (una para publicaciones y otra para comentarios), o usar una exclusividad entre dos relaciones reacciona_pub y reacciona_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 tipo incluye el valor ‘TEXTO’.
  • Mensajes privados. El enunciado no los pide. Si se añadieran, serían una entidad MENSAJE con dos relaciones con USUARIO (emisor y receptor), o una entidad CONVERSACION con 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… con oficio como 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 es EMPLEADO → COMERCIAL (parcial) con el atributo comision y la relación atiende, 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_linea en 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_unitario no es redundante con productos.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).
  • localidad y ciudad como atributos. Si se quisiera normalizar las poblaciones, aparecería una entidad LOCALIDAD. 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ébil ESTANCIA_DEPT con la historia. Restricción: la estancia vigente coincide con el departamento actual; las estancias no se solapan.
  • (b) La relación N:M asignado se reifica en la entidad ASIGNACION con 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)