Durante el desarrollo de un proyecto realizamos una serie de métricas que mostraban el mensaje ERROR cuando se filtraba un reporte por un campo que no tenía relación.

El caso concretamente fue respecto a la visualización de su stock. Este podía estar en distintos Store, por ejemplo: un Store inicial (Store) y dos Store destino (Store 1 y Store 2).

Pero sólo interesaba que el cálculo de las métricas se realizara sobre el stock del Store inicial y además en el caso de que estas métricas fueran filtradas en un reporte (PowerBI) por cualquiera de los campos de los otros dos Store, en una situación normal ésto nos mostraría un producto cartesiano con el valor total del stock, indicativo de que la relación entre la métrica y el atributo (filtro) no es correcta.

El problema que presentan estos reportes es que no siempre quien visualiza o hace un reporte tiene estos conocimientos técnicos, por ello se pensó que en las situaciones en las que se utilizara un atributo sin relación con la métrica, esta debería arrojar un mensaje o simplemente un “ERROR”.

Para resolver este problema, desarrollamos una serie de métricas utilizando las siguientes funciones DAX que a continuación explicamos:

  • IF (logical_test>, <value_if_true>, value_if_false)

Comprueba si una condición proporcionada en el primer argumento se cumple, si la condición se cumple devuelve un valor y si no se cumple devuelve otro.

Si un valor se omite devuelve una cadena varia (“”).

  • CALCULATE (<expression>, <filter1>, <filter2>…)

Evalúa una expresión que es modificada por los filtros que se especifiquen.

El primer argumento es la expresión a evaluar (una medida).

El resto de argumento son los filtros a aplicar sobre el primer argumento.

Esta función devuelve el valor del primer argumento (La medida).

  • ISFILTERED (<columnName>)

Devuelve TRUE cuando columnName se filtra directamente. Si no hay ningún filtro en la columna o si el filtrado se produce porque se filtra una columna diferente en la misma tabla o en una tabla relacionada devuelve FALSE.

ColumnName se filtra directamente cuando el filtro o los filtros se aplican sobre la columna; se dice que una columna tiene un filtro cruzado cuando un filtro aplicado a otra columna en la misma tabla o en una tabla relacionada afecta a columnName la columna al filtrarla también.

  • ISCROSSFILTERED (<columnName>)

Es similar a ISFILTERED pero con la diferencia que devuelve TRUE cuando se está filtrando columnName y otra columna en la misma u otra tabla relacionada.

  • USERELATIONSHIP (<columnName1>, <columnName2>)

Especifica la relación que se utiliza en un cálculo especifico como la que existe entre ColumnName1 y ColumnName2.

ColumnName1 es el nombre de una columna que representa el lado de la relación que se usará.

ColumnName2 es el nombre de una columna que representa el lado de búsqueda de la relación que se usará.

Una vez explicadas las anteriores funciones, creamos un ejemplo, paso a paso, de una de las métricas que desarrollamos para este caso:

1. Realizaremos el filtro que contenga el mensaje de Error:

IF(ISFILTERED('NombreTabla1'[IdTabla1) || ISFILTERED('NombreTable2'[IdTabla2]); "ERROR";

El problema de hacerlo de la anterior forma, utilizando ISFILTERED es que si de las tablas Tabla1 o Tabla2 utilizan un valor distinto al IdTabla1 o IdTabla2 este mensaje de “ERROR” no se mostrará y volverá a entregar un producto cartesiano. Como vemos en la siguiente imagen, el campos utilizado ha sido Name Store y no funciona como deseamos.

Por ello, contemplamos como mejor opción utilizar la función ISCROSSFILTERED.

IF(ISCROSSFILTERED('NombreTabla1'[IdTabla1) || ISCROSSFILTERED('NombreTable2'[IdTabla2]); "ERROR";

2. Posteriormente, añadimos la función CALCULATE para poder añadir por último un filtro al cálculo.

CALCULATE ( (CalculoMétrica);

3. Dentro de la función CALCULATE agregamos el filtro utilizando la función USERELATIONSHIP, con la que, le decimos que utilice únicamente la relación con la Tabla0.

USERELATIONSHIP (
        	 'TablaHechos'[id_sk_Tabla0];
                 'NombreTabla0'[sk_Tabla0]
                )

Finalmente la métrica quedaría de la siguiente forma:

NombreMétrica := 
IF(ISCROSSFILTERED('NombreTabla1'[IdTabla1) || ISCROSSFILTERED('NombreTable2'[IdTabla2]); "ERROR";
       CALCULATE ( (CalculoMétrica);
            USERELATIONSHIP (
        		     'TablaHechos'[id_sk_Tabla0];
        		    'NombreTabla0'[sk_Tabla0]
            )
       )
)

Y el resultado sería el siguiente:

CONCLUSIÓN

Para terminar, como reflexión, esta es una solución para que los usuarios de negocio sin conocimientos técnicos reconozcan cuando un campo no es correcto. Si el modelo crece continuamente, realizar estas métricas por este motivo puede suponer un alto coste de mantenimiento, ya que, se debería añadir el resto de dimensiones a las métricas para conservar el mensaje que queremos mostrar.

 

 

José Fco Moreno Fdez
Últimas entradas de José Fco Moreno Fdez (ver todo)