🔗

Funciones de Relaciones

RELATED, RELATEDTABLE, USERELATIONSHIP, CROSSFILTER — controla cómo fluye el contexto entre tablas

Intermedio Avanzado
🔍

Cómo funcionan las relaciones en DAX 🤔

En Power BI, las relaciones conectan las tablas de tu modelo de datos. Entender cómo navegar por estas relaciones es fundamental para escribir DAX avanzado.

Modelo estrella típico

      'Clientes'          'Productos'         'Calendario'
          │                    │                    │
          │ (uno)              │ (uno)              │ (uno)
          │                    │                    │
          └────────────────────┼────────────────────┘
                               │
                          'Ventas' (muchos)
                    [IDCliente, IDProducto, Fecha, Importe]
            

Características de las relaciones en Power BI

  • Dirección de filtro: De la tabla "uno" hacia la tabla "muchos" (por defecto). Los filtros aplicados a Clientes, Productos o Calendario fluyen automáticamente a Ventas.
  • Cardinalidad:
    • Uno a muchos (1:N) — la más común
    • Muchos a muchos (N:M) — requiere configuración especial
    • Uno a uno (1:1) — poco común
  • Estado:
    • Activa — solo puede haber UNA relación activa entre dos tablas
    • Inactiva — relaciones adicionales que deben activarse con USERELATIONSHIP

Las 4 funciones de relaciones en DAX

  • RELATED — navega "río arriba" (de la tabla muchos a la tabla uno)
  • RELATEDTABLE — navega "río abajo" (de la tabla uno a la tabla muchos)
  • USERELATIONSHIP — activa una relación inactiva temporalmente
  • CROSSFILTER — cambia la dirección de filtrado o desactiva el filtrado cruzado
Analogía kawaii 🌸
Piensa en las relaciones como canales de agua:
  • El agua (los filtros) fluye desde la montaña (tabla "uno") hacia el río (tabla "muchos")
  • RELATED es como nadar río arriba — vas desde Ventas hacia Productos/Clientes
  • RELATEDTABLE es como dejarse llevar río abajo — vas desde Productos/Clientes hacia Ventas
  • USERELATIONSHIP abre una compuerta secundaria
  • CROSSFILTER cambia la dirección del canal
⬇️

RELATEDTABLE — navegar de uno a muchos

RELATEDTABLE(<tabla>)
Intermedio — Avanzado
Devuelve la tabla relacionada navegando "hacia el lado muchos" de la relación. Desde una tabla de dimensión, obtiene todas las filas de la tabla de hechos que están relacionadas con la fila actual. Equivalente al INNER JOIN en sentido inverso.

Ejemplo en columna calculada de dimensión

// En la tabla Productos, contar cuántas ventas tiene cada producto:
'Productos'[Nº Ventas] = COUNTROWS(RELATEDTABLE('Ventas'))
// Para cada producto, devuelve las filas de Ventas relacionadas con ese producto
// y cuenta cuántas hay

// Total vendido de cada producto (en columna calculada de la dimensión):
'Productos'[Total Vendido] =
SUMX(RELATEDTABLE('Ventas'), 'Ventas'[Importe])

// Ticket máximo de cada cliente:
'Clientes'[Ticket Máximo] =
MAXX(RELATEDTABLE('Ventas'), 'Ventas'[Importe])

// Fecha del último pedido de cada cliente:
'Clientes'[Última Compra] =
MAXX(RELATEDTABLE('Ventas'), 'Ventas'[Fecha])

// ¿El producto se vendió alguna vez con descuento?
'Productos'[Alguna Vez con Descuento] =
IF(
    COUNTROWS(
        FILTER(RELATEDTABLE('Ventas'), 'Ventas'[Descuento] > 0)
    ) > 0,
    TRUE,
    FALSE
)

Diferencia RELATEDTABLE vs CALCULATETABLE

// RELATEDTABLE: solo funciona en contexto de fila, navega por relación existente
'Productos'[Total] = SUMX(RELATEDTABLE('Ventas'), 'Ventas'[Importe])

// CALCULATETABLE: más flexible, funciona en medidas, puede añadir filtros adicionales
Total Ventas Producto X =
SUMX(
    CALCULATETABLE('Ventas', 'Productos'[ID] = "P001"),
    'Ventas'[Importe]
)
🔄

USERELATIONSHIP — activar relaciones inactivas

USERELATIONSHIP(<columna1>, <columna2>)
Avanzado
Modificador de CALCULATE que activa una relación INACTIVA para la duración del cálculo. Power BI solo permite una relación activa entre dos tablas — si necesitas filtrar por una segunda relación, debes marcarla como inactiva en el modelo y activarla temporalmente con USERELATIONSHIP.

Caso de uso típico — múltiples relaciones fecha

Escenario muy común: tabla Ventas con dos columnas de fecha:

  • FechaPedido — fecha en que se realizó el pedido
  • FechaEntrega — fecha en que se entregó

Solo UNA puede ser la relación activa con el Calendario. La otra debe ser inactiva y se activa con USERELATIONSHIP cuando la necesitas.

// En el modelo:
// Activa:   Ventas[FechaPedido] → Calendario[Date]
// Inactiva: Ventas[FechaEntrega] → Calendario[Date] (marcada como inactiva)

// Medida usando la relación activa (FechaPedido):
Pedidos por Fecha = COUNTROWS('Ventas')
// Esto filtra por FechaPedido automáticamente

// Medida usando la relación INACTIVA (FechaEntrega):
Entregas por Fecha =
CALCULATE(
    COUNTROWS('Ventas'),
    USERELATIONSHIP('Ventas'[FechaEntrega], 'Calendario'[Date])
)
// USERELATIONSHIP activa temporalmente la relación de FechaEntrega
// para este cálculo específico

// Ambas en el mismo informe con el mismo slicer de fecha:
// El slicer filtra la fecha, USERELATIONSHIP determina cuál columna se usa

Ejemplo 2 — tabla de roles en RRHH

// Tabla Empleados con:
// ManagerID → relación inactiva con Empleados[EmpleadoID]
// para la jerarquía de reporting

Nombre Manager =
CALCULATE(
    MAX('Empleados'[Nombre]),
    USERELATIONSHIP('Empleados'[ManagerID], 'Empleados'[EmpleadoID])
)
⚠️ Limitaciones de USERELATIONSHIP
USERELATIONSHIP solo puede activar relaciones que ya existen en el modelo (aunque sean inactivas). No puede crear relaciones nuevas en tiempo de ejecución — para eso existe TREATAS.

Además, USERELATIONSHIP suspende la relación activa entre esas dos tablas mientras dura el cálculo.
↔️

CROSSFILTER — cambiar la dirección de filtrado

CROSSFILTER(<columna1>, <columna2>, <dirección>)
Avanzado
Modificador de CALCULATE que cambia la dirección de filtrado cruzado de una relación para la duración del cálculo. Los valores posibles de dirección son:
  • NONE — desactiva el filtrado por esta relación
  • ONEWAY — filtra solo en la dirección "uno → muchos" (comportamiento normal)
  • BOTH — filtra en ambas direcciones (bidireccional)

Por qué es importante

Por defecto, los filtros fluyen de la tabla "uno" a la tabla "muchos". A veces necesitas que el filtro fluya en la dirección contraria.

En lugar de cambiar el modelo permanentemente (lo cual puede crear ambigüedad), usa CROSSFILTER para activar la bidireccionalidad solo cuando la necesitas.

Ejemplo — contar clientes que compraron un producto específico

// Sin CROSSFILTER, el filtro de Productos no llega a Clientes
// (el filtro va Productos → Ventas, pero no Ventas → Clientes de vuelta)

// ❌ Puede no funcionar según el modelo:
Clientes que compraron Laptop =
CALCULATE(
    DISTINCTCOUNT('Ventas'[IDCliente]),
    'Productos'[Nombre] = "Laptop Pro"
)

// ✅ Con CROSSFILTER (el filtro fluye Productos → Ventas → Clientes):
Clientes que compraron Laptop v2 =
CALCULATE(
    DISTINCTCOUNT('Clientes'[ID]),
    CROSSFILTER('Ventas'[IDProducto], 'Productos'[ID], BOTH),
    'Productos'[Nombre] = "Laptop Pro"
)

Ejemplo — desactivar una relación temporalmente

// Calcular el total sin que una relación filtre:
Total Sin Filtro Region =
CALCULATE(
    [Total Ventas],
    CROSSFILTER('Ventas'[IDRegion], 'Regiones'[ID], NONE)
)
🏆 Best practice: bidireccionalidad selectiva
La bidireccionalidad permanente en el modelo puede crear ambigüedad y reducir el rendimiento (especialmente en modelos complejos).

El patrón recomendado por SQLBI: mantener las relaciones unidireccionales en el modelo y usar CROSSFILTER(BOTH) solo cuando sea estrictamente necesario en una medida concreta.
🏆

Patrones avanzados con relaciones

Patrón 1 — Ventas del mes de entrega (relación inactiva)

Ventas por Entrega =
CALCULATE(
    SUM('Ventas'[Importe]),
    USERELATIONSHIP('Ventas'[FechaEntrega], 'Calendario'[Date])
)

Patrón 2 — Contar clientes únicos que compraron una categoría

Clientes por Categoría =
CALCULATE(
    DISTINCTCOUNT('Clientes'[ID]),
    CROSSFILTER('Ventas'[IDCliente], 'Clientes'[ID], BOTH)
)

Patrón 3 — Segmentación de clientes en columna calculada

// Añadir el segmento de cliente a cada venta (para análisis rápido):
'Ventas'[Segmento] = RELATED('Clientes'[Segmento])
'Ventas'[País] = RELATED('Clientes'[País])
'Ventas'[Categoría Producto] = RELATED('Productos'[Categoría])

Patrón 4 — Tabla de hechos enriquecida para análisis

Ventas Enriquecidas =
ADDCOLUMNS(
    'Ventas',
    "País Cliente", RELATED('Clientes'[País]),
    "Segmento", RELATED('Clientes'[Segmento]),
    "Categoría", RELATED('Productos'[Categoría]),
    "Año", RELATED('Calendario'[Año]),
    "Trimestre", RELATED('Calendario'[Trimestre])
)
🚀 Siguiente paso
Controlas el flujo de datos entre tablas — eso es manejar DAX a nivel avanzado. A continuación, las funciones de Información — el grupo IS... que te ayuda a entender en qué contexto está evaluándose tu medida.