</>waridocu

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.

bases-de-datosmuchos-a-muchosentidad-intermediauid-barradobarker

El problema: dónde guardo la nota

El modelo del instituto quedó con esta relación:

texttext
  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:

texttext
┌──────────────┐        ┌──────────────────┐        ┌──────────────┐
│ 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:

AlumnoCursofechanota
Ana RuizJava Básico2026-03-018.5
Ana RuizBases de Datos2026-03-05(null)
Beto SosaJava Básico2026-03-026.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.

Importante

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:

NombreDe dónde viene
Entidad intermediaEstá en el medio de las otras dos
Entidad asociativaAsocia dos entidades
Entidad de intersecciónCada 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.
Tip

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”.

texttext
┌──────────────┐        ┌──────────────────┐        ┌──────────────┐
│ ALUMNO       │        │ INSCRIPCION      │        │ CURSO        │
├──────────────┤        ├──────────────────┤        ├──────────────┤
│ # id_alumno  │──────┼<│ * fecha          │>┼──────│ # codigo     │
│ * nombre     │        │ o nota           │        │ * nombre     │
└──────────────┘   ↑    └──────────────────┘   ↑    └──────────────┘
                   │                           │
              barra: forma parte del UID de INSCRIPCION

Ese 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:

sqlsql
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.

Advertencia

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:

texttext
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.

texttext
┌──────────────┐     ┌──────────────────────┐     ┌──────────────┐
│ 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ónEntidad intermediaSus atributos propios
Alumnos ↔ CursosINSCRIPCIONfecha, nota
Facturas ↔ ProductosLINEA_FACTURAcantidad, precio unitario
Socios ↔ LibrosPRESTAMOfecha de retiro, fecha de devolución
Actores ↔ PelículasREPARTOpersonaje, orden en los créditos
Usuarios ↔ RolesASIGNACIONfecha desde, fecha hasta
Médicos ↔ PacientesCONSULTAfecha, 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.

Nota

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

🎯 ¿Cómo se modela esto?

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.”

  • Correcto. La relación es N:M (un pasajero vuela muchas veces, un vuelo lleva muchos pasajeros) y hay datos del vínculo — asiento y fecha de compra no pertenecen ni al pasajero ni al vuelo, sino a la combinación. Y "reserva" es la palabra que el propio negocio usa.

  • Es un atributo multivaluado, que ya se descartó al hablar de atributos. Y aunque se aceptara, seguiría sin haber dónde poner el asiento de cada vuelo.

  • Describe bien el negocio en el modelo conceptual, pero deja los dos problemas sin resolver: el asiento y la fecha de compra no tienen dónde vivir, y una base relacional no puede implementar la relación tal cual.

  • Limitaría cada vuelo a un solo pasajero. Es el mismo error de poner la cardinalidad "uno" donde el negocio dice "muchos": un vuelo lleva cientos de personas.

✏️ Nombra la entidad intermedia

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_devolucion

💡 Resuelve el modelo completo

Modela 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.

texttext
┌──────────────────┐    ┌──────────────────┐    ┌──────────────────┐
│ 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.
  • numero es un UID natural de SALA — el cine ya numera sus salas, no hace falta inventar un id.
  • clasificacion es * 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.