Bases de datos
Relaciones muchos a muchos
Por qué una relación N:M no se puede implementar tal cual, cómo se resuelve con una entidad intermedia, y qué significa la barra atravesada en la relación.
El problema: dónde guardo la nota
El modelo del instituto quedó con esta relación:
ALUMNO CURSO
┌──────────┐ ┌──────────┐
│ │>- - - - - - - - - - - - <│ │
└──────────┘ se inscribe en └──────────┘Es una descripción correcta del negocio, pero tiene dos problemas que se resuelven de un solo golpe.
Problema 1: la fecha y la nota no tienen dónde ir. El enunciado decía que de cada inscripción interesa la fecha y la nota final. ¿Dónde se guardan? En ALUMNO no, porque un alumno tiene una nota distinta por cada curso. En CURSO tampoco, por lo mismo. Son datos que no pertenecen a ninguna de las dos entidades, sino al vínculo entre ellas.
Problema 2: no se puede implementar así. Una tabla ALUMNO no puede tener una columna con “todos los cursos de este alumno” —eso sería un atributo multivaluado, justo lo que el modelado evita— y la tabla CURSO tiene el mismo impedimento simétrico. No hay lugar donde poner el vínculo.
La solución es la misma para los dos: convertir la relación en una entidad.
La entidad intermedia
Una relación N:M se resuelve creando una entidad nueva en el medio, y reemplazando la relación original por dos relaciones 1:N que apuntan hacia ella:
┌──────────────┐ ┌──────────────────┐ ┌──────────────┐
│ ALUMNO │ │ INSCRIPCION │ │ CURSO │
├──────────────┤ ├──────────────────┤ ├──────────────┤
│ # id_alumno │───────<│ * fecha │>───────│ # codigo │
│ * nombre │ │ o nota │ │ * nombre │
│ * email │ │ │ │ * horas │
└──────────────┘ └──────────────────┘ └──────────────┘Fíjate en cómo cambiaron las patas de gallo: antes estaban en los extremos de afuera, ahora las dos apuntan hacia el centro. Leído en voz alta:
Cada INSCRIPCIÓN debe corresponder a uno y solo un ALUMNO. Cada INSCRIPCIÓN debe corresponder a uno y solo un CURSO. Cada ALUMNO puede tener una o más INSCRIPCIONES. Cada CURSO puede tener una o más INSCRIPCIONES.
Y los dos problemas desaparecen: fecha y nota viven en INSCRIPCION, que es exactamente de lo que hablan, y cada instancia es una fila con un alumno y un curso — nada multivaluado en ningún lado.
Cada instancia representa un hecho concreto:
| Alumno | Curso | fecha | nota |
|---|---|---|---|
| Ana Ruiz | Java Básico | 2026-03-01 | 8.5 |
| Ana Ruiz | Bases de Datos | 2026-03-05 | (null) |
| Beto Sosa | Java Básico | 2026-03-02 | 6.0 |
Ana aparece dos veces con cursos distintos, Java Básico aparece dos veces con alumnos distintos, y la nota vacía de Bases de Datos es simplemente un curso que todavía no terminó — el o de nota justificado.
Toda relación N:M se resuelve así, sin excepciones. Es la transformación más mecánica de todo el modelado: ves pata de gallo en los dos extremos, creas una entidad en el medio, y das vuelta las dos patas de gallo hacia adentro. Si estás dibujando el modelo lógico para después generar tablas, hazlo siempre.
Cómo se llama la entidad del medio
Vas a encontrar varios nombres para lo mismo:
| Nombre | De dónde viene |
|---|---|
| Entidad intermedia | Está en el medio de las otras dos |
| Entidad asociativa | Asocia dos entidades |
| Entidad de intersección | Cada instancia es un cruce de las dos |
| Tabla puente (junction table) | Su nombre al llegar al modelo físico |
Para nombrarla, hay dos estrategias:
- El nombre del negocio, si existe. “Inscripción”, “Reserva”, “Préstamo”, “Matrícula”, “Línea de pedido”. Siempre es preferible: significa algo para quien lee.
- La combinación de las dos, si el negocio no tiene una palabra:
ALUMNO_CURSO. Es feo pero honesto.
Si te cuesta encontrar el nombre, prueba con la pregunta “¿cómo llama el cliente al papel que se firma cuando esto pasa?”. Casi siempre existe una palabra: uno no “alumno-cursea”, uno se inscribe. Y encontrar ese sustantivo suele traer de regalo los atributos que faltaban.
El UID de la entidad intermedia
Aquí aparece un detalle propio de estas entidades. ¿Qué identifica a una inscripción? Normalmente, la combinación del alumno y el curso: Ana no puede inscribirse dos veces en Java Básico.
Pero id_alumno y codigo no son atributos de INSCRIPCION: llegan a través de las relaciones. Barker tiene una notación específica para eso: la barra atravesada en la relación, que se lee “esta relación forma parte del identificador”.
┌──────────────┐ ┌──────────────────┐ ┌──────────────┐
│ ALUMNO │ │ INSCRIPCION │ │ CURSO │
├──────────────┤ ├──────────────────┤ ├──────────────┤
│ # id_alumno │──────┼<│ * fecha │>┼──────│ # codigo │
│ * nombre │ │ o nota │ │ * nombre │
└──────────────┘ ↑ └──────────────────┘ ↑ └──────────────┘
│ │
barra: forma parte del UID de INSCRIPCIONEse UID se llama barrado (barred UID), y al llegar al modelo físico se convierte en una clave primaria compuesta por las dos claves foráneas:
PRIMARY KEY (id_alumno, codigo)Lo que eso garantiza es concreto: la base rechaza físicamente dos inscripciones del mismo alumno al mismo curso. No hace falta escribir código que lo controle.
Ese UID barrado prohíbe repetir el par. A veces eso es justo lo que quieres… y a veces no. Si el negocio permite que un alumno recurse una materia el año siguiente, ese UID lo impide. Las dos salidas son agregar anio como tercer atributo del UID, o darle a INSCRIPCION un identificador artificial propio y marcar la combinación como única solo donde corresponda. Decidirlo requiere preguntar, no adivinar.
Cuándo la entidad intermedia ya existía
No siempre hay que “descubrirla”: muy seguido ya estaba en el enunciado y no se la había reconocido como entidad. El caso más claro es una factura:
Enunciado: "Una factura incluye varios productos, y cada producto
aparece en muchas facturas. De cada producto en una factura se
guarda la cantidad y el precio al que se vendió."Ese “de cada producto en una factura se guarda…” es la señal inequívoca: hay datos del vínculo, así que hay entidad intermedia. Y tiene nombre propio en el negocio: línea de factura o detalle.
┌──────────────┐ ┌──────────────────────┐ ┌──────────────┐
│ FACTURA │ │ LINEA_FACTURA │ │ PRODUCTO │
├──────────────┤ ├──────────────────────┤ ├──────────────┤
│ # nro │────┼<│ * cantidad │>┼───│ # cod_prod │
│ * fecha │ │ * precio_unitario │ │ * nombre │
└──────────────┘ └──────────────────────┘ │ * precio │
└──────────────┘El precio_unitario en la línea es una decisión de diseño que vale la pena entender: PRODUCTO ya tiene un precio, así que parece repetido. No lo es. El precio del producto es el de hoy; el de la línea es el que se cobró ese día. Si mañana sube el precio, las facturas viejas tienen que seguir diciendo lo que realmente se cobró. Guardar el valor histórico en el momento de la transacción es un patrón que aparece en casi todo sistema de ventas.
Casos frecuentes de N:M
| Relación | Entidad intermedia | Sus atributos propios |
|---|---|---|
| Alumnos ↔ Cursos | INSCRIPCION | fecha, nota |
| Facturas ↔ Productos | LINEA_FACTURA | cantidad, precio unitario |
| Socios ↔ Libros | PRESTAMO | fecha de retiro, fecha de devolución |
| Actores ↔ Películas | REPARTO | personaje, orden en los créditos |
| Usuarios ↔ Roles | ASIGNACION | fecha desde, fecha hasta |
| Médicos ↔ Pacientes | CONSULTA | fecha, diagnóstico |
Mira la última columna: en todos los casos hay atributos propios. Esa es la mejor confirmación de que la entidad intermedia era una entidad de verdad y no solo un artificio técnico.
Cuando genuinamente no hay ningún atributo del vínculo —por ejemplo, las etiquetas de un artículo— la entidad intermedia se crea igual, con solo las dos relaciones y el UID barrado. Se sigue necesitando por el problema 2: sin ella no hay dónde guardar el vínculo. Y curiosamente, esas suelen ganar atributos con el tiempo (quién puso la etiqueta, cuándo), confirmando la decisión a posteriori.
Pon a prueba
Una aerolínea: “Un pasajero puede tomar muchos vuelos y un vuelo lleva muchos pasajeros. De cada pasajero en un vuelo necesitamos saber el asiento asignado y la fecha en que compró el pasaje.”
Completa el modelo de una biblioteca: los socios retiran libros, y de cada retiro se guarda cuándo se llevó y cuándo se devolvió (que al principio no se sabe).
SOCIO ────< >──── LIBRO
PRESTAMO
fecha_retiro
fecha_devolucionModela esto por completo, con símbolos y relaciones:
Un cine proyecta películas en varias salas. De cada película se guarda título, duración y clasificación. De cada sala se guarda su número y su capacidad. Una película se proyecta en varias salas y en cada sala se proyectan varias películas; de cada proyección interesa el horario y el precio de la entrada.
Hay una N:M entre PELICULA y SALA, con datos del vínculo (horario y precio), así que corresponde una entidad intermedia. El negocio ya tiene la palabra: función.
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ PELICULA │ │ FUNCION │ │ SALA │
├──────────────────┤ ├──────────────────┤ ├──────────────────┤
│ # id_pelicula │───┼<│ # horario │>┼──│ # numero │
│ * titulo │ │ * precio │ │ * capacidad │
│ * duracion │ └──────────────────┘ └──────────────────┘
│ * clasificacion │
└──────────────────┘Leído en voz alta:
Cada FUNCIÓN debe proyectar una y solo una PELÍCULA. Cada FUNCIÓN debe ocurrir en una y sola una SALA. Cada PELÍCULA puede tener una o más FUNCIONES. Cada SALA puede tener una o más FUNCIONES.
Las decisiones que hay que poder defender:
- El UID de FUNCION es barrado e incluye
horario. Las dos relaciones solas no alcanzan: la misma película se proyecta en la misma sala varias veces al día, así que el par (película, sala) se repetiría. Agregando el horario, la terna identifica una función y solo una. numeroes un UID natural de SALA — el cine ya numera sus salas, no hace falta inventar un id.clasificaciones*porque legalmente toda película exhibida tiene que tenerla.- Los extremos hacia PELICULA y SALA son continuos: una función sin película o sin sala no significa nada. Los extremos hacia FUNCION son punteados: una película recién cargada todavía no tiene funciones programadas, y una sala en refacción tampoco.
Un detalle que suele pasarse por alto: si el cine quisiera vender entradas numeradas, aparecería otra entidad —ENTRADA o BUTACA— colgando de FUNCION. Que el modelo admita ese crecimiento sin rehacerse es señal de que la estructura era la correcta.
Siguiente paso
El modelo lógico está terminado. Falta la traducción mecánica al mundo de las tablas: qué se convierte en clave primaria, dónde aparece cada clave foránea y qué CREATE TABLE sale de todo esto — ver Del modelo lógico a las tablas.