🌉

Tablas Puente (Bridge Tables)

Cómo resolver las relaciones muchos a muchos correctamente en Power BI sin ambigüedad de filtros ni duplicaciones de datos

Avanzado
🌉

¿Qué es una tabla puente?

La intermediaria que resuelve el caos N:N

Una tabla puente (también llamada bridge table, junction table o tabla de intersección) es una tabla que actúa de intermediaria entre dos tablas que tienen una relación muchos a muchos (N:N). Es como una lista de "quién está con quién" que permite que Power BI filtre correctamente sin duplicar datos ni crear ambigüedades.

En la teoría de bases de datos relacionales, las relaciones N:N no se pueden representar directamente: siempre se descomponen en dos relaciones 1:N a través de una tabla puente. Power BI introduce relaciones N:N nativas, pero con limitaciones importantes que veremos en la sección siguiente.

💡 Analogía kawaii 🌸

Imagina que tienes una lista de estudiantes 👩‍🎓 y una lista de clubs 🌸. Una estudiante puede pertenecer a varios clubs, y un club puede tener varias estudiantes. La tabla puente es la lista de inscripciones: quién está apuntada a qué club. Sin esa lista, ¿cómo sabrías qué clubs tiene cada estudiante? Exactamente — no podrías.

💡 Las relaciones N:N directas en Power BI (sin tabla puente) técnicamente funcionan, pero pueden generar resultados incorrectos en ciertos contextos de filtro si no las entiendes a fondo. La tabla puente es siempre la solución más robusta, predecible y recomendada por Microsoft.
⚠️

El problema muchos a muchos

Por qué la relación N:N directa es problemática

Supón que tienes productos y categorías con una relación muchos a muchos: un producto puede pertenecer a varias categorías, y una categoría puede contener varios productos.

Sin tabla puente — relación N:N directa:

  'Productos'                       'Categorias'
  ┌──────────────────┐             ┌──────────────────┐
  │ IDProducto       │─────────────│ IDCategoria      │
  │ NomProducto      │  N        N │ NomCategoria     │
  └──────────────────┘             └──────────────────┘
         │
         │ 1:N
         ▼
  'Ventas'
  ┌──────────────────┐
  │ IDProducto (FK)  │
  │ Importe          │
  └──────────────────┘

  Problema: ¿cómo filtra Power BI?
  "Laptop Pro" pertenece a "Electrónica" Y a "Informática".
  El filtro puede propagar el contexto de forma ambigua.

El resultado incorrecto:

Si una venta de Laptop Pro tiene Importe = 1.000 € y el producto aparece en 2 categorías, al filtrar por categoría y sumar SUM(Ventas[Importe]) el motor puede — en determinados modelos y contextos — devolver 2.000 € en lugar de 1.000 €, porque la fila de ventas aparece "visible" desde ambas categorías al mismo tiempo.

⚠️ Ojo con esto: La relación muchos a muchos nativa de Power BI puede parecer que funciona correctamente en los visuales básicos, pero en ciertos contextos de filtro cruzado produce duplicaciones silenciosas. El peligro es que los números parecen plausibles y nadie los cuestiona hasta que alguien cruza los datos con otra fuente. Siempre prueba tus métricas con valores conocidos antes de publicar.

La solución: tabla puente

El modelo correcto con tabla puente interpuesta

La solución es introducir una tabla intermedia que descomponga la relación N:N en dos relaciones 1:N, cada una perfectamente manejable por el motor VertiPaq.

  'Productos'       'ProductoCategorias'         'Categorias'
  ┌────────────┐    ┌────────────────────────┐    ┌────────────────┐
  │ IDProd  PK │─1─▶│ IDProducto  FK         │◀─N─│ IDCategoria PK │
  │ NomProd    │    │ IDCategoria FK         │  1 │ NomCategoria   │
  └────────────┘    └────────────────────────┘    └────────────────┘
        │
        │ 1:N
        ▼
  'Ventas'
  ┌────────────┐
  │ IDProducto │
  │ Importe    │
  └────────────┘

Flujo de filtros paso a paso:

  1. El usuario filtra Categorias[NomCategoria] = "Electrónica"
  2. El filtro pasa a ProductoCategorias (tabla puente) y devuelve los IDProducto que pertenecen a Electrónica
  3. Esos IDProducto filtran la tabla Ventas a través de su relación con Productos
  4. SUM(Ventas[Importe]) devuelve el resultado correcto, sin duplicados

Reglas de oro de la tabla puente:

  • Contiene solo claves foráneas (FK), nunca métricas ni atributos descriptivos
  • Se relaciona con cada dimensión en N:1 (muchos registros de puente → una dimensión)
  • Se relaciona con la tabla de hechos de forma indirecta, pasando por la dimensión principal
  • No se usa directamente en visuales — es infraestructura invisible del modelo
📌 Buena práctica: La tabla puente debe estar en modo Import (no DirectQuery) siempre que sea posible, para que el motor VertiPaq pueda comprimir correctamente sus columnas de claves enteras y ejecutar los cruces con máxima eficiencia.
🛍️

Caso real: productos multi-categoría

Tienda online con categorías múltiples por producto

Escenario: tienda online con 500 productos, donde cada producto puede pertenecer a 1, 2 o 3 categorías. Se necesita analizar las ventas por categoría sin duplicar los importes.

Dim_Productos
IDProductoNombre
P001Laptop Pro
P002Ratón USB
P003Auriculares
Dim_Categorias
IDCategoriaNombre
C01Electrónica
C02Informática
C03Accesorios
ProductoCategorias (tabla puente)
IDProductoIDCategoria
P001C01Laptop → Electrónica
P001C02Laptop → Informática
P002C02Ratón → Informática
P002C03Ratón → Accesorios
P003C01Auriculares → Electrónica
P003C03Auriculares → Accesorios

Crear la tabla puente en Power Query (M)

Cuando el origen tiene las categorías como texto separado por comas, Power Query puede generar la tabla puente automáticamente:

Power Query — M

// Origen: columna "Categorias" contiene "Electrónica, Informática"
// Queremos generar una fila por cada categoría asignada al producto

let
    Origen           = TablaProductos,
    SplitCategorias  = Table.TransformColumns(
                         Origen,
                         {{"Categorias",
                           each Text.Split(_, ", "),
                           type list}}
                       ),
    Expand           = Table.ExpandListColumn(
                         SplitCategorias, "Categorias"
                       ),
    SeleccionarCols  = Table.SelectColumns(
                         Expand,
                         {"IDProducto", "Categorias"}
                       ),
    Renombrar        = Table.RenameColumns(
                         SeleccionarCols,
                         {{"Categorias", "IDCategoria"}}
                       )
in
    Renombrar
💡 Tip kawaii: El patrón Text.Split + ExpandListColumn es uno de los más usados en Power Query para normalizar datos "sucios". Una columna con valores separados por comas se convierte en filas individuales en tres pasos. Guárdalo en tu repertorio — lo usarás constantemente.
👩‍💼

Caso real: empleados multi-rol

RRHH con empleadas que tienen más de un rol

Escenario: el equipo de RRHH necesita analizar costes de personal por departamento y rol. Algunas personas tienen más de un rol asignado (por ejemplo, Analista de BI y Scrum Master). El objetivo es que el coste mensual de cada persona no se duplique al filtrar por rol.

Dim_Empleadas
IDEmpleadaNombreCoste Mensual
E001Laura3.500 €
E002Marta4.000 €
Dim_Roles
IDRolNombreRol
R01Analista de BI
R02Scrum Master
R03Data Engineer
EmpleadaRoles (tabla puente)
IDEmpleadaIDRol
E001R01Laura → Analista de BI
E001R02Laura → Scrum Master también
E002R01Marta → Analista de BI
E002R03Marta → Data Engineer también

Diagrama del modelo completo:

  'Empleadas' ──1:N──▶ 'EmpleadaRoles' ◀──N:1── 'Roles'
       │              (tabla puente)
       │ 1:N
       ▼
  'CostesPersonal'
  ┌──────────────────┐
  │ IDEmpleada (FK)  │
  │ Mes              │
  │ ImporteCoste     │
  └──────────────────┘
⚠️ Ojo con esto: Si aplicaras SUM(Empleadas[CosteMensual]) filtrando directamente por rol sin tabla puente, Laura (que tiene R01 y R02) contaría dos veces: 3.500 + 3.500 = 7.000 €. Con la tabla puente correctamente configurada, el filtro de rol actúa sobre EmpleadaRoles y el coste de Laura se contabiliza una sola vez.

DAX con tablas puente

A) Medidas estándar — funcionan automáticamente

Con la tabla puente en su sitio y las relaciones correctamente definidas, las medidas básicas funcionan sin código DAX adicional. Power BI propaga el filtro a través de la cadena de relaciones de forma transparente.

DAX — medida estándar

// Esta medida es suficiente. No necesitas nada especial.
Total Ventas = SUM(Ventas[Importe])
// Al filtrar por Categorias[NomCategoria]:
//   1. Filtro → ProductoCategorias (puente)
//   2. → Productos (relación 1:N)
//   3. → Ventas (relación 1:N)
// Resultado: correcto, sin duplicados.

// Para verificar que no hay duplicación:
Productos Únicos en Categoría =
DISTINCTCOUNT(ProductoCategorias[IDProducto])
// Compara este número con lo esperado en cada categoría.

B) DAX explícito — casos avanzados

A veces necesitas filtrar la propia tabla puente o navegar la relación de forma explícita.

DAX — filtro explícito sobre la tabla puente

// Ventas de productos que tienen AL MENOS UNA categoría
// cuyo nombre empieza por "Elec" (ej: "Electrónica"):
Ventas Electro =
CALCULATE(
    [Total Ventas],
    FILTER(
        ProductoCategorias,
        LEFT(RELATED(Categorias[NomCategoria]), 4) = "Elec"
    )
)

// Categorías únicas representadas en las ventas del mes actual:
Categorias Vendidas Este Mes =
CALCULATE(
    DISTINCTCOUNT(ProductoCategorias[IDCategoria]),
    FILTER(
        Ventas,
        Ventas[Fecha] >= DATE(YEAR(TODAY()), MONTH(TODAY()), 1)
    )
)

C) Tabla puente calculada en DAX (sin Power Query)

Si no puedes generar la tabla puente en el origen, puedes crearla como tabla calculada en DAX. Úsalo solo como último recurso.

DAX — tabla calculada (tabla puente)

// Tabla calculada que reconstruye la puente desde una tabla de mapeo:
ProductoCategorias_Calc =
SELECTCOLUMNS(
    FILTER(
        CROSSJOIN(
            VALUES(Productos[IDProducto]),
            VALUES(Categorias[IDCategoria])
        ),
        // Mantener solo las combinaciones válidas según tabla de mapeo:
        NOT ISBLANK(
            LOOKUPVALUE(
                MapeoCategoria[IDCategoria],
                MapeoCategoria[IDProducto], [IDProducto],
                MapeoCategoria[IDCategoria], [IDCategoria]
            )
        )
    ),
    "IDProducto", [IDProducto],
    "IDCategoria", [IDCategoria]
)
📌 Buena práctica: Siempre que puedas, crea la tabla puente en Power Query (o en el origen de datos), no en DAX. Las tablas calculadas en DAX se recalculan en cada refresh usando el motor de fórmulas, que es más lento. Las tablas de Power Query son procesadas directamente por VertiPaq con compresión óptima.
🐛

Errores frecuentes

Los cuatro tropiezos clásicos con tablas puente

Error 01

Relación N:N directa sin tabla puente

Síntoma: Las métricas parecen correctas en algunos visuales, pero se duplican al cruzar con otras dimensiones o al usar ciertos filtros.
Solución: Interponer tabla puente y verificar los totales con DISTINCTCOUNT contra un recuento manual conocido.

Error 02

Incluir métricas o atributos en la tabla puente

Síntoma: Se añade Importe, Precio o NombreCategoria a la tabla puente para "tener todo en un sitio".
Solución: La tabla puente solo tiene FKs. Las métricas van en la tabla de hechos; los atributos descriptivos van en las dimensiones.

Error 03

Dirección de filtro incorrecta en las relaciones

Síntoma: Filtrar por categoría no afecta a la tabla de ventas, o afecta a tablas que no debería.
Solución: Verificar que la relación Productos ↔ ProductoCategorias tiene dirección de filtro desde Productos hacia ProductoCategorias. En la UI: la flecha apunta hacia la tabla puente.

Error 04

Usar la tabla puente directamente en visuales

Síntoma: Se arrastra ProductoCategorias[IDCategoria] a un visual en lugar de Categorias[NomCategoria]. El resultado son IDs en lugar de nombres, y el agrupamiento puede ser incorrecto.
Solución: Usa siempre la dimensión real (Categorias) en los visuales. La tabla puente es infraestructura interna del modelo, no está pensada para exponerse al usuario.
🎯

Resumen y siguiente paso

Los 4 puntos que debes llevarte

📋 Checklist tabla puente

  1. Una tabla puente resuelve relaciones N:N sin duplicar métricas ni crear ambigüedades de filtro
  2. Solo contiene claves foráneas (FK) — nunca métricas ni atributos descriptivos propios
  3. Créala en Power Query (o en el origen) siempre que sea posible; evita tablas calculadas DAX salvo necesidad
  4. Verifica el resultado con DISTINCTCOUNT y valores conocidos antes de publicar
🚀 ¡Lo estás haciendo genial! Las tablas puente son uno de los patrones más profesionales del modelado de datos — no todo el mundo las domina. Ahora que las tienes bajo control, el siguiente paso natural es entender cuándo normalizar y cuándo desnormalizar tu modelo. Explora Relaciones entre Tablas para profundizar en la dirección de filtro y la bidireccionalidad, o revisa las buenas prácticas del Esquema Estrella para ver cómo encajan las tablas puente en una arquitectura profesional. 🌸