Hace tiempo vengo encontrándome con fallas de modelado en las que las personas rompen la direccionalidad de dos tablas o más por factores que pueden ser resueltos de otro modo. También casos de múltiples tablas de fecha.
Ciertamente una de las reglas de oro al modelar es mantener las direccionalidades "simples", es decir, evitar el "ida y vuelta". Este tipo de dirección podría funcionar en casos muy puntuales de modelos estrella bien definidos, pero si mi modelo tiene copos de nieve o es multi estrella podría complicarse. Aún más si no tenemos un modelo definido con la teoría y solo cargamos tablas. Al igual que el many-to-many no controlado puede ocasionar problemas, la dirección importa y podría provocar ambigüedad en el modelo (cuando las relaciones comienzan a desactivarse porque hay múltiples caminos a un número o el número no se filtra como esperamos porque los filtros direccionados rompen las reglas de negocio).
Otra situación recurrente surge si necesitamos trackear algún valor por una fecha distinta, tenemos soluciones viables más estables que crear muchas tablas fechas. Hablo de la posibilidad de usar una relación inactiva.
Vamos a charlar de dos funciones que nos ayudarían a mantener la consistencia del modelo y no crear tablas innecesarias.
Direccionalidad
Existen diversas razones y formas para resolver problemas de direccionalidad. Naturalmente lo primero que debemos preguntarnos es ¿Por qué queremos hacer algo de ida y vuelta? siempre debemos preguntarnos porque hacemos algo que rompe las buenas prácticas o teorías de modelado. Si no sabías que mantener relaciones 1-* con direccionalidad simple era una buena práctica, siempre puedes revisar los conceptos de modelado que proponen SQLBI o el libro Star Schema.
Dependiendo la razón por la cual queremos llegar a esto, podemos encontrarnos con distintas soluciones. Usualmente los casos para prender esta opción prohibida son dos.
- Necesitamos que una dimensión filtre a otra y activamos la bidireccionalidad.
- Necesitamos que un valor, número, kpi específico tengo bidireccionalidad. Ya sea porque se creó en una dimensión o por efectos de un copo de nieve.
Para el primero de los casos podremos nutrirnos de la solución del siguiente post: https://blog.ladataweb.com.ar/sincronizar-filtros-entre-dimensiones/
En el segundo de los casos vamos a utilizar la función CROSSFILTER. Dicha función especifica la dirección de filtrado cruzado que se va a usar en un cálculo de una relación que existe entre dos columnas. Esto quiere decir que podemos cambiar la direccionalidad de una relación exclusivamente para una medida. De esta forma podríamos ejecutar una medida considerando una relación. Ejemplo, tenemos una tabla histórica de una granularidad mayor a la de una dimensión:

Por cada father guardamos el paso por sus tres estados. Sin embargo, los filtros del informe siempre serán dados por la dimensión. Entonces, ¿Cómo podríamos contar cuantos father tenemos con status 2 para cierta fecha si la columna "Father Name" no filtraría nuestra tabla histórica?
Para un escenario como este, podríamos aplicar CROSSFILTER de la siguiente manera.
Medida con otra direccionalidad =
CALCULATE(
[Operación de calculo]
, CROSSFILTER( DIM[father_id], HistoricalDIM_Father[father_id], BOTH)
Como podemos apreciar, CROSSFILTER se forma con dos primeros argumentos que son las columnas participantes de la relación actual y un tercer argumento que indica la direccionalidad del filtro cruzado. Cambiando este a BOTH podríamos modificar el comportamiento para esa medida exclusivamente sin llevar ambigüedad al modelo.
Relaciones Inactivas
Existe otra función de interés que podría ayudarnos a trabajar estos problemas en el modelado. Cuando por X razón no podemos mantener activa una relación en el modelo, ya sea porque antes hicimos malas prácticas y no podemos prenderlas por ambigüedad o porque necesitamos usar más de una columna como parte de la relación (teoría conocida en Kimbal como Role-Playing Dimension)
Si necesitamos usar una relación inactiva para una medida podemos hacerlo con la función USERELATIONSHIP. Por si no sabías si se puede poner más de una relación por tabla, solo que una sola queda activa.

En este escenario tenemos una tabla fecha relacionada activamente con la fecha de venta. También está relacionada inactivamente con las fechas de entrega (delivery) y vencimiento (due). Si bien en instancias de filtros de datos siempre se efectúa el camino por la fecha de venta, podemos crear una medida que no sea así. Podemos crear una medida que utilice una relación inactiva. Veamos cómo sería eso:
Cantidad de ventas entregadas =
CALCULATE(
COUNTROWS( InternetSales )
, USERELATIONSHIP( TablaFecha[Fecha], InternetSales[Delivery Date] )
Como podemos apreciar, la función pide especificar las columnas involucradas en la relación inactiva para ser usada. Esto significa que el conteo de filas de la tabla será afectado de otra forma si usamos la TablaFecha para filtrar la medida. Esto daría mucho beneficio porque mantendríamos consistencia e integridad de datos hablando de un único periodo en todo el informe y números ajustados a eso. No necesitaríamos dos tablas fecha con dos filtros. También podríamos integrar números distintos en un misma visualizaciones usando columnas de TablaFecha.
Llegamos así al final del post donde aprendimos el uso de dos poderosas funciones de DAX que nos ayudarían a no romper buenas prácticas de modelado tan solo por un simple requerimiento en una medida o kpi.