</>waridocu

Bases de datos

Banco de ejercicios

Ejercicios de modelado entidad-relación ordenados por dificultad, desde poner símbolos hasta resolver un enunciado completo, todos con solución explicada.

bases-de-datosejerciciospracticamodeladobarker

Cómo usar esta página

Los ejercicios están ordenados de menos a más y agrupados por lo que ejercitan. No hace falta hacerlos en orden ni todos de una vez: si un tema concreto no te queda claro, ve directo a su nivel.

Todos tienen la solución explicada, no solo la respuesta. Intenta resolverlo en papel antes de abrirla — el modelado se aprende equivocándose y viendo por qué, no leyendo modelos correctos.

NivelQué ejercitaPágina de referencia
1Los símbolos #, *, oLos símbolos
2Entidad contra atributoEntidades y atributos
3Cardinalidad y opcionalidadRelaciones
4Muchos a muchosRelaciones N:M
5Enunciados completosTodo lo anterior
Tip

Para los de nivel 5, date un límite de tiempo: 15 minutos por enunciado. Modelar es una habilidad que se practica bajo presión (en un parcial, en una reunión con un cliente), y tener el reloj corriendo obliga a decidir en vez de dar vueltas.


Nivel 1 · Los símbolos

✏️ 1.1 — Concesionaria

Cada auto se identifica por su patente. La marca y el modelo se cargan siempre. El color y el kilometraje a veces faltan cuando el auto todavía no llegó al local.

AUTO
 patente
 marca
 modelo
 color
 kilometraje

✏️ 1.2 — Empleados

El legajo identifica al empleado. Nombre y fecha de ingreso se cargan al contratarlo. La fecha de egreso solo se conoce cuando la persona se va, y el email corporativo se asigna días después del ingreso.

EMPLEADO
 legajo
 nombre
 fecha_ingreso
 fecha_egreso
 email_corporativo

🎯 1.3 — ¿Sirve como UID?

En una entidad PRODUCTO, ¿sirve descripcion como identificador único?

  • Aunque hoy no se repita, nada lo impide: dos productos parecidos pueden terminar con la misma descripción. Y sobre todo, una descripción se reescribe cuando alguien la mejora — un UID no puede cambiar.

  • Correcto. Falla en las tres condiciones: no es estable (se reescribe), no está garantizada como única, y bien puede faltar en un producto recién dado de alta. Corresponde un código de producto o un id artificial.

  • NOT NULL solo resuelve la obligatoriedad. Quedan sin resolver la unicidad y, sobre todo, la estabilidad: el día que alguien corrija una falta de ortografía en la descripción, cambiaría el identificador y con él todas las referencias.

  • La conclusión es correcta pero el motivo no. Un UID de texto es perfectamente válido: el ISBN de un libro o la patente de un auto lo son. Lo que descalifica a la descripción es que cambia, no que sea texto.

💡 1.4 — Corrige los símbolos

Este modelo tiene cinco errores. Encuéntralos.

texttext
┌────────────────────────────────┐
│ RESERVAS                       │
├────────────────────────────────┤
│ o id_reserva                   │
│ * fecha_reserva                │
│ * fecha_cancelacion            │
│ # email_cliente                │
│ * cantidad_noches              │
│ * precio_total                 │
└────────────────────────────────┘

(precio_total se calcula multiplicando las noches por la tarifa de la habitación.)

1. RESERVAS está en plural. Debe ser RESERVA: la entidad nombra qué es cada instancia.

2. o id_reserva. Es el identificador, así que debe ser #. Un UID nunca puede ser opcional.

3. * fecha_cancelacion. La gran mayoría de las reservas nunca se cancelan. Marcarla obligatoria haría imposible guardar una reserva vigente. Debe ser o.

4. # email_cliente. Dos problemas: como UID falla (el mismo cliente hace muchas reservas, así que ni siquiera es único), y el cliente no va como atributo suelto sino como entidad relacionada.

5. * precio_total es un atributo derivado. Se calcula con cantidad_noches y la tarifa, así que guardarlo duplica información que puede quedar inconsistente.

Aunque acá hay matiz: el precio de una reserva es un valor histórico, igual que el precio_unitario de una factura. Si la tarifa de la habitación sube después, la reserva tiene que seguir diciendo lo que se cobró. En ese caso guardarlo es correcto — pero entonces hay que guardar el precio acordado, no recalcularlo. La decisión depende del negocio, y lo que se evalúa es que sepas justificarla.

texttext
┌────────────────────────────────┐
│ RESERVA                        │
├────────────────────────────────┤
│ # id_reserva                   │
│ * fecha_reserva                │
│ * cantidad_noches              │
│ * precio_acordado              │
│ o fecha_cancelacion            │
└────────────────────────────────┘

     └── relación hacia CLIENTE y hacia HABITACION

Nivel 2 · Entidad o atributo

🎯 2.1 — La marca de un producto

“De cada producto guardamos nombre, precio y stock. Cada producto es de una marca, y de cada marca nos interesa su país de origen y su sitio web.”

  • Correcto. El país de origen y el sitio web son datos de la marca, no del producto, y un atributo no puede tener atributos. Además el nombre de la marca se repetiría idéntico en cientos de productos.

  • Alcanzaría si de la marca no se guardara nada más. Pero el enunciado pide país de origen y sitio web, y esos datos no tienen dónde ir dentro de un atributo suelto.

  • Repite el país y el sitio web de cada marca en todos sus productos. Corregir el sitio web de una marca obligaría a actualizar cientos de filas, y bastaría con que una fallara para que la base se contradiga.

  • Esta decisión pertenece al modelo lógico, donde el motor todavía no existe. La misma respuesta vale para Oracle, PostgreSQL o MySQL.

🎯 2.2 — El color de una remera

“De cada prenda guardamos tipo, talle, color y precio.” No se guarda ningún otro dato sobre los colores.

  • Correcto, y es el caso simétrico del anterior. No hay datos sobre el color, no se lo consulta por separado, y una entidad COLOR con un único atributo agregaría un JOIN a cada consulta sin aportar nada.

  • Que un valor se repita no basta por sí solo: también se repiten los precios y las fechas. Lo que justifica sacar algo a una entidad es tener datos propios o necesitar controlar qué valores existen — y acá no se pide ninguna de las dos.

  • Sería correcto si el enunciado dijera que una misma prenda se ofrece en varios colores. Como está, cada prenda tiene un color, así que es un atributo simple.

  • No es una regla absoluta. Sacar un valor a su propia entidad tiene un costo (una tabla más, un JOIN más) que solo se justifica cuando hay algo que ganar: datos propios del valor, o la garantía de que solo existan los valores permitidos.

💡 2.3 — Encuentra las entidades escondidas

Este modelo puso todo en una sola entidad. Sepáralo correctamente.

texttext
┌────────────────────────────────────┐
│ CONSULTA_MEDICA                    │
├────────────────────────────────────┤
│ # id_consulta                      │
│ * fecha                            │
│ * nombre_paciente                  │
│ * telefono_paciente                │
│ * obra_social_paciente             │
│ * nombre_medico                    │
│ * especialidad_medico              │
│ * matricula_medico                 │
│ * diagnostico                      │
└────────────────────────────────────┘

Los sufijos _paciente y _medico son la pista: cada vez que varios atributos comparten un sufijo, ahí hay una entidad escondida.

texttext
┌──────────────────────┐   ┌──────────────────┐   ┌──────────────────────┐
│ PACIENTE             │   │ CONSULTA         │   │ MEDICO               │
├──────────────────────┤   ├──────────────────┤   ├──────────────────────┤
│ # id_paciente        │──<│ * fecha          │>──│ # matricula          │
│ * nombre             │   │ * diagnostico    │   │ * nombre             │
│ o telefono           │   └──────────────────┘   │ * especialidad       │
│ o obra_social        │                          └──────────────────────┘
└──────────────────────┘

Lo que se gana:

  • El teléfono de un paciente se escribe una vez, no una por consulta. Si cambia, se corrige en un solo lugar.
  • Un paciente puede existir aunque nunca se haya atendido (se registró y todavía no vino).
  • Un médico también, y su especialidad no se repite en cada consulta que atendió.
  • Borrar una consulta no borra al paciente ni al médico.

Detalles de las decisiones:

  • matricula es el UID natural de MEDICO — es única por ley, obligatoria y no cambia. Es uno de los pocos casos donde un UID natural es claramente la mejor opción.
  • telefono y obra_social pasaron a o. Estaban como * porque en la tabla plana toda consulta tenía que llenar todo; ya separados, un paciente puede no tener obra social o no haber dejado teléfono.
  • CONSULTA es la entidad intermedia de la N:M entre pacientes y médicos, con sus atributos propios fecha y diagnostico. Su UID no es barrado: el mismo paciente puede volver al mismo médico muchas veces, así que corresponde un id_consulta propio, o incluir la fecha en el UID.

Nivel 3 · Cardinalidad y opcionalidad

✏️ 3.1 — Lee la relación

Un país tiene muchas ciudades y cada ciudad está en un solo país. Un país recién creado en el sistema podría no tener ciudades cargadas todavía.

Cada CIUDAD  pertenecer a  PAIS.

Cada PAIS  contener  CIUDADES.

🎯 3.2 — ¿Qué extremo es opcional?

“Un cliente puede hacer varios pedidos. Todo pedido corresponde a un cliente. Un cliente que se registra hoy todavía no hizo ninguno.”

  • Correcto. Un cliente recién registrado todavía no compró nada, así que su lado admite cero pedidos (punteado). Un pedido sin cliente, en cambio, no significa nada: ese lado es continuo.

  • Obligaría a que todo cliente tuviera al menos un pedido, y entonces no se podría registrar a nadie antes de su primera compra — justo el orden en que pasan las cosas.

  • Está invertido. Permitiría pedidos sin cliente (¿de quién es?) y exigiría que todo cliente tuviera pedidos.

  • El lado del cliente sí lo es, pero dejar opcional el lado del pedido permitiría guardar pedidos huérfanos, sin nadie a quien facturarlos ni a quien entregarlos.

💡 3.3 — Tres relaciones mal dibujadas

Léelas en voz alta y di qué está mal en cada una.

texttext
a)  EMPLEADO ──────────────────< DEPARTAMENTO

b)  AUTOR >- - - - - - - - - - - LIBRO

c)  FACTURA ──────────────────── LINEA_FACTURA

(Reglas del negocio: un empleado trabaja en un departamento y un departamento tiene muchos empleados; un libro tiene un autor y un autor escribe muchos libros; una factura tiene varias líneas.)

a) EMPLEADO ─────< DEPARTAMENTO

Se lee: “Cada empleado debe trabajar en uno o más departamentos.”

La pata de gallo está del lado equivocado. Los “muchos” son los empleados, no los departamentos. Correcto:

texttext
DEPARTAMENTO - - - - - - - -< EMPLEADO

b) AUTOR >- - - - - LIBRO

Se lee: “Cada libro puede ser escrito por uno o más autores” y “cada autor puede escribir uno y solo un libro.”

Los dos extremos están invertidos: la pata de gallo debería tocar a LIBRO, y el trazo del lado de AUTOR debería ser continuo (todo libro tiene autor). Correcto:

texttext
AUTOR - - - - - - - -< LIBRO

c) FACTURA ──────── LINEA_FACTURA

Se lee: “Cada factura debe tener una y sola una línea.”

Falta la pata de gallo: una factura tiene varias líneas. Y además el lado de LINEA_FACTURA debería ser obligatorio pero el de FACTURA continuo también tiene sentido acá (una factura sin ninguna línea no significa nada). Correcto:

texttext
FACTURA ───────────────< LINEA_FACTURA

El patrón de los tres: el error es siempre el mismo, poner la pata de gallo en la entidad que la frase nombra primero en vez de en la que tiene muchas instancias. Leer en voz alta lo detecta cada vez.

🎯 3.4 — ¿Cuándo un 1:1 está bien?

Una empresa modeló EMPLEADO y FICHA_MEDICA como dos entidades en relación 1:1. La ficha guarda grupo sanguíneo, alergias y contacto de emergencia; solo la tiene cargada el personal que pasó por el examen preocupacional, y solo el área de salud puede consultarla.

¿Está bien esa decisión?

  • Correcto. Son los dos motivos válidos para un 1:1, y acá se dan los dos juntos: la mayoría de los empleados no tendría esos datos cargados, y separarlos permite que solo el área de salud ocupacional acceda a esa tabla.

  • Es la sospecha correcta como regla general — la mayoría de los 1:1 son entidades partidas sin motivo. Pero acá hay dos razones concretas que la vencen: la opcionalidad de los datos y el control de acceso separado.

  • Cambiar la cardinalidad no responde al problema planteado, e introduce una complejidad que el negocio no pide. Si en el futuro hicieran falta fichas históricas, ese sería el momento de reconsiderarlo.

  • El tamaño es un tercer motivo válido para partir una entidad, pero no es el que decide acá. Aunque la ficha tuviera dos campos, la opcionalidad y el acceso restringido ya justifican la separación.


Nivel 4 · Muchos a muchos

✏️ 4.1 — La entidad intermedia

Los actores trabajan en películas; de cada participación se guarda el personaje que interpreta y su orden en los créditos. Completa la resolución de la N:M.

ACTOR  ────<    >────  PELICULA

REPARTO
 personaje
 orden_creditos

🎯 4.2 — ¿Hace falta entidad intermedia?

Un blog: “Un artículo puede tener varias etiquetas y una etiqueta puede estar en varios artículos.” No se guarda ningún dato adicional sobre esa asociación.

  • Correcto. Aunque no haya ni un atributo del vínculo, sin la entidad intermedia no hay dónde guardar la asociación: ni ARTICULO ni ETIQUETA pueden tener una lista adentro. La entidad se crea igual, con solo las dos relaciones y el UID barrado.

  • Ese atributo sería multivaluado — una lista dentro de una columna. Es exactamente lo que el modelado evita, y hace que buscar "todos los artículos con esta etiqueta" sea incómodo y lento.

  • Los atributos propios son un motivo adicional, no la condición. El motivo principal es estructural: sin la entidad no hay lugar físico donde guardar el vínculo.

  • La cantidad de datos no cambia la estructura correcta. Con diez artículos o con diez millones, la relación necesita el mismo modelo.

💡 4.3 — Resuelve la N:M

Resuelve este modelo por completo:

Un supermercado tiene proveedores (nombre, CUIT, teléfono) y productos (código, nombre, stock). Un proveedor abastece varios productos y un producto puede venir de varios proveedores. De cada combinación proveedor–producto interesa el precio que cobra ese proveedor y en cuántos días entrega.

texttext
┌──────────────────┐   ┌────────────────────┐   ┌──────────────────┐
│ PROVEEDOR        │   │ ABASTECIMIENTO     │   │ PRODUCTO         │
├──────────────────┤   ├────────────────────┤   ├──────────────────┤
│ # cuit           │──┼<│ * precio          │>┼─│ # codigo         │
│ * nombre         │   │ * dias_entrega     │   │ * nombre         │
│ o telefono       │   └────────────────────┘   │ * stock          │
└──────────────────┘                            └──────────────────┘

Lectura en voz alta:

Cada ABASTECIMIENTO debe corresponder a un y solo un PROVEEDOR. Cada ABASTECIMIENTO debe corresponder a un y solo un PRODUCTO. Cada PROVEEDOR puede abastecer uno o más PRODUCTOS. Cada PRODUCTO puede ser abastecido por uno o más PROVEEDORES.

Las decisiones:

  • cuit es un buen UID natural de PROVEEDOR: único por ley, obligatorio para facturar, y no cambia. Es de los pocos casos donde no hace falta un id artificial.
  • precio y dias_entrega van en la entidad intermedia, y esa es la prueba de que hacía falta: no son del proveedor (cobra distinto por cada producto) ni del producto (cada proveedor lo cobra distinto). Son de la combinación.
  • El UID de ABASTECIMIENTO es barrado: el par (proveedor, producto) identifica un acuerdo, y no tiene sentido que el mismo proveedor tenga dos precios distintos para el mismo producto al mismo tiempo.
  • stock está en PRODUCTO, no en ABASTECIMIENTO. Es el stock total del supermercado, no depende de quién lo trajo. Ponerlo en la intermedia sería un error clásico: obligaría a sumar filas para saber cuánto hay.

Extensión, si quieres seguir: ¿dónde pondrías el historial de precios, para saber qué cobraba ese proveedor el año pasado? La respuesta es una entidad más colgando de ABASTECIMIENTO, con la fecha desde la que rige cada precio — el mismo patrón que aparece cada vez que un dato necesita conservar su historia.

🧩 Ordena el código

Ordena los pasos para resolver una relación muchos a muchos.

  1. Poner en ella los atributos propios del vínculo
  2. Buscarle el nombre que usa el negocio para ese hecho
  3. Reemplazar la relación original por dos relaciones 1:N hacia la nueva entidad
  4. Crear una entidad nueva entre las dos
  5. Decidir si su UID es barrado o si necesita un identificador propio
  6. Buscar si hay datos que pertenecen al vínculo y no a ninguna de las dos entidades
  7. Detectar la pata de gallo en los dos extremos de la relación
  8. Girar las dos patas de gallo hacia la entidad del medio


Nivel 5 · Enunciados completos

💡 5.1 — Hotel

Un hotel tiene habitaciones, cada una con su número, tipo (simple, doble, suite) y tarifa por noche. Los huéspedes se registran con nombre, documento, email y teléfono opcional. Un huésped hace reservas: cada reserva es de una habitación, con fecha de entrada y de salida, y puede estar confirmada o cancelada. Un huésped puede tener varias reservas a lo largo del tiempo, y una habitación se reserva muchas veces.

Modela con símbolos, relaciones y opcionalidad. Justifica el UID de cada entidad.

texttext
┌──────────────────────┐   ┌──────────────────────┐   ┌──────────────────────┐
│ HUESPED              │   │ RESERVA              │   │ HABITACION           │
├──────────────────────┤   ├──────────────────────┤   ├──────────────────────┤
│ # id_huesped         │──<│ # id_reserva         │>──│ # numero             │
│ * documento          │   │ * fecha_entrada      │   │ * tipo               │
│ * nombre             │   │ * fecha_salida       │   │ * tarifa_noche       │
│ * email              │   │ * estado             │   └──────────────────────┘
│ o telefono           │   └──────────────────────┘
└──────────────────────┘

Lectura:

Cada RESERVA debe corresponder a un y solo un HUÉSPED. Cada RESERVA debe ser de una y sola una HABITACIÓN. Cada HUÉSPED puede tener una o más RESERVAS. Cada HABITACIÓN puede tener una o más RESERVAS.

Los UID:

  • HABITACION: numero, natural. El hotel ya numera sus habitaciones y ese número no cambia.
  • HUESPED: id_huesped artificial, con documento marcado como único aparte. El documento parece un buen UID natural, pero falla con extranjeros de documentación distinta y con cargas erróneas que hay que corregir.
  • RESERVA: id_reserva propio, NO barrado. Este es el punto interesante: un UID barrado con (huésped, habitación) prohibiría que el mismo huésped vuelva a la misma habitación en otro viaje. Agregar fecha_entrada lo arreglaría, pero un id propio es más simple y es lo que el hotel usa para hablar con el cliente (“su reserva número 4471”).

Otras decisiones:

  • estado es * con valores confirmada/cancelada. Toda reserva tiene un estado desde que se crea.
  • tarifa_noche está en HABITACION, pero ojo: si las tarifas cambian por temporada, el precio de una reserva vieja se perdería. Un modelo más completo guardaría el precio_acordado en RESERVA, por el mismo motivo del precio_unitario de una factura.
  • Lo que el modelo no impide: dos reservas confirmadas de la misma habitación en fechas superpuestas. Ninguna restricción del modelo E-R puede expresar eso; se controla con lógica de aplicación o con restricciones específicas del motor. Reconocer ese límite es parte de saber modelar.
💡 5.2 — Plataforma de cursos online

Una plataforma ofrece cursos, cada uno con título, descripción y precio. Cada curso lo creó un instructor (nombre, email, biografía). Un curso se divide en lecciones ordenadas, cada una con título y duración en minutos. Los usuarios (nombre, email, fecha de registro) se inscriben en cursos pagando un precio, y la plataforma registra cuándo se inscribieron y qué porcentaje del curso completaron. Además, un usuario puede dejar una reseña de un curso en el que está inscripto, con puntaje y comentario.

texttext
┌────────────────┐
│ INSTRUCTOR     │
├────────────────┤
│ # id_instructor│
│ * nombre       │
│ * email        │
│ o biografia    │
└────────────────┘
        │ crea

┌────────────────┐         ┌──────────────────┐
│ CURSO          │────────<│ LECCION          │
├────────────────┤         ├──────────────────┤
│ # id_curso     │         │ # id_leccion     │
│ * titulo       │         │ * titulo         │
│ * precio       │         │ * duracion_min   │
│ o descripcion  │         │ * orden          │
└────────────────┘         └──────────────────┘
    │        │
    │        └──────────────────┐
    │                           │
┌──────────────────┐     ┌──────────────────┐
│ INSCRIPCION      │     │ RESEÑA           │
├──────────────────┤     ├──────────────────┤
│ # id_inscripcion │     │ # id_reseña      │
│ * fecha          │     │ * puntaje        │
│ * precio_pagado  │     │ * fecha          │
│ * porcentaje     │     │ o comentario     │
└──────────────────┘     └──────────────────┘
    │                           │
┌────────────────┐              │
│ USUARIO        │──────────────┘
├────────────────┤
│ # id_usuario   │
│ * nombre       │
│ * email        │
│ * fecha_registro│
└────────────────┘

Las cinco decisiones que definen este modelo:

1. LECCION es 1:N desde CURSO, no N:M. Una lección pertenece a un solo curso. El atributo orden es lo que permite mostrarlas en secuencia; sin él, no habría forma de saber cuál va primero.

2. INSCRIPCION resuelve la N:M entre USUARIO y CURSO, con tres atributos propios: fecha, precio_pagado y porcentaje. El precio_pagado está separado del precio del curso a propósito: si el curso sube de precio o se vende con descuento, hay que conservar lo que efectivamente se cobró.

3. RESEÑA es una entidad aparte, no atributos de INSCRIPCION. Es la decisión más discutible del modelo y hay que poder defenderla: como no toda inscripción genera una reseña, meter puntaje y comentario en INSCRIPCION dejaría esas columnas vacías en la mayoría de las filas. Separarla también permitiría después permitir más de una, o moderarlas.

4. La regla “solo se puede reseñar un curso en el que estás inscripto” no la expresa el modelo. Se podría forzar haciendo que RESEÑA cuelgue de INSCRIPCION en vez de conectar con USUARIO y CURSO por separado. Es una alternativa mejor si esa regla es estricta — y notar la diferencia entre las dos opciones es exactamente lo que se evalúa en este ejercicio.

5. porcentaje es un atributo derivado guardado a propósito. Se podría calcular contando lecciones vistas, pero eso requeriría otra entidad (LECCION_VISTA) que el enunciado no pide. Guardarlo es una simplificación consciente, no un descuido — y si más adelante hiciera falta saber qué lecciones vio, esa entidad aparecería y el porcentaje pasaría a calcularse.

💡 5.3 — Aerolínea (difícil)

Una aerolínea opera vuelos identificados por un número (AR1234), con aeropuerto de origen, de destino y horarios de salida y llegada. Cada vuelo se opera con un avión (matrícula, modelo, cantidad de asientos). Los pasajeros (documento, nombre, email) compran pasajes para un vuelo en una fecha determinada: el mismo vuelo AR1234 sale todos los días, y cada salida es distinta. De cada pasaje se guarda el asiento y la clase (turista o ejecutiva). Los aeropuertos tienen código IATA, nombre y ciudad.

Este enunciado tiene una trampa. Encuéntrala.

La trampa está en “el mismo vuelo AR1234 sale todos los días, y cada salida es distinta”.

Hay que distinguir dos cosas que el lenguaje común llama igual:

  • VUELO — la ruta programada: AR1234 va de EZE a MAD, sale 22:00, llega 15:30. Es una definición que se repite todos los días.
  • VUELO_PROGRAMADO (o salida) — la instancia concreta: AR1234 del 15 de marzo de 2026, con ese avión y esos pasajeros.

Un pasaje no es para “AR1234”: es para AR1234 de un día determinado. Y el avión asignado puede cambiar de un día para otro. Modelar sin esa separación hace imposible saber quién viajó en qué salida.

texttext
┌──────────────────┐
│ AEROPUERTO       │
├──────────────────┤
│ # codigo_iata    │───┐ origen
│ * nombre         │───┤ destino
│ * ciudad         │   │
└──────────────────┘   │

┌──────────────────────┴───┐        ┌──────────────────┐
│ VUELO                    │        │ AVION            │
├──────────────────────────┤        ├──────────────────┤
│ # numero                 │        │ # matricula      │
│ * hora_salida            │        │ * modelo         │
│ * hora_llegada           │        │ * cant_asientos  │
└──────────────────────────┘        └──────────────────┘
          │                                  │
          └────────────┐        ┌────────────┘
                ┌──────────────────────┐
                │ VUELO_PROGRAMADO     │
                ├──────────────────────┤
                │ # id_vuelo_prog      │
                │ * fecha              │
                │ * estado             │
                └──────────────────────┘

                ┌──────────────────────┐    ┌──────────────────┐
                │ PASAJE               │────│ PASAJERO         │
                ├──────────────────────┤    ├──────────────────┤
                │ # id_pasaje          │    │ # id_pasajero    │
                │ * asiento            │    │ * documento      │
                │ * clase              │    │ * nombre         │
                │ * precio             │    │ * email          │
                └──────────────────────┘    └──────────────────┘

Lo demás que hay que notar:

  • AEROPUERTO se relaciona dos veces con VUELO, una como origen y otra como destino. Dos relaciones distintas entre las mismas dos entidades es perfectamente válido, y es la única forma de expresarlo. En el modelo relacional se convierte en dos claves foráneas hacia la misma tabla.
  • PASAJE es la entidad intermedia entre PASAJERO y VUELO_PROGRAMADO, con asiento, clase y precio como atributos del vínculo.
  • El UID de VUELO_PROGRAMADO podría ser barrado con (número de vuelo, fecha), que es único y significativo. Un id propio también sirve y es más cómodo de referenciar desde PASAJE.
  • codigo_iata es un UID natural excelente: internacionalmente único, estable y ya usado en todos lados. Es el ejemplo de libro de cuándo no inventar un id artificial.

La lección general: cuando un enunciado dice “cada X se repite” o “el mismo X pero en otra fecha”, casi siempre hay dos entidades donde parecía haber una: la plantilla y la ocurrencia. Pasa con vuelos y salidas, con materias y cursadas, con clases de gimnasio y sus sesiones, con series y episodios.


Lista de verificación

Antes de dar un modelo por terminado, revisa esto. Es la misma lista con la que se corrige un parcial:

Verificar
Toda entidad tiene un UID (#)
Los nombres de entidad están en singular
Cada atributo tiene su símbolo: #, * u o
Ningún atributo es multivaluado (nada de tel1, tel2)
Ningún atributo compuesto quedó sin descomponer
Ningún atributo derivado se guarda sin motivo
No queda ninguna relación N:M sin resolver
Cada relación tiene un nombre que es un verbo concreto
Cada relación se leyó en voz alta en las dos direcciones
Cada extremo opcional se justificó con un caso real
Ningún dato del negocio quedó sin lugar donde guardarse

Si las once dan bien, el modelo está listo para pasar a tablas.

Y después

Esta sección cubre el modelado entidad-relación con Data Modeler. Los pasos naturales a partir de acá son la normalización (las formas normales, que llegan a conclusiones parecidas por un camino más formal) y el SQL para consultar las tablas que acabas de diseñar.