Optimización de los cálculos de DAX

Completado

Cuando el analizador de rendimiento identifica una consulta DAX lenta, el siguiente paso es optimizar el propio DAX. El uso ineficaz de DAX es una de las causas más comunes de un rendimiento deficiente del modelo semántico. Los pequeños cambios en las definiciones de medida pueden reducir significativamente los tiempos de consulta.

Usar variables para eliminar cálculos repetidos

Una de las técnicas de optimización DAX más eficaces es usar variables (VAR / RETURN). Cuando una fórmula evalúa la misma expresión más de una vez, el motor lo vuelve a calcular cada vez. Las variables almacenan el resultado de una expresión una vez y la reutilizan, lo que puede reducir el tiempo de consulta aproximadamente a la mitad para las fórmulas con subexpresiones repetidas.

Tenga en cuenta esta medida de crecimiento anual:

Sales YoY Growth % =
DIVIDE(
    ([Sales] - CALCULATE([Sales], PARALLELPERIOD('Date'[Date], -12, MONTH))),
    CALCULATE([Sales], PARALLELPERIOD('Date'[Date], -12, MONTH))
)

La PARALLELPERIOD expresión se evalúa dos veces: una vez en el numerador y una vez en el denominador. La reescritura con una variable elimina la redundancia:

Sales YoY Growth % =
VAR SalesPriorYear =
    CALCULATE([Sales], PARALLELPERIOD('Date'[Date], -12, MONTH))
RETURN
    DIVIDE(([Sales] - SalesPriorYear), SalesPriorYear)

El resultado es el mismo, pero el motor evalúa SalesPriorYear solo una vez. Las variables también mejoran la legibilidad: los nombres de variables descriptivos facilitan la comprensión y el mantenimiento de fórmulas complejas.

Sugerencia

Las variables también simplifican la depuración. Puede cambiar temporalmente la RETURN expresión para generar solo el valor de la variable, lo que le permite inspeccionar los resultados intermedios sin volver a escribir toda la fórmula.

Comprender FILTER frente a KEEPFILTERS

Cómo aplicar modificaciones de filtro en DAX afecta directamente al rendimiento. La FILTER función recorre en iteración una fila de tabla por fila para evaluar una condición. Cuando se aplica a una tabla grande, esta iteración puede ser costosa.

-- Expensive: FILTER iterates the entire Sales table
High Value Sales =
CALCULATE(
    [Total Sales],
    FILTER(Sales, Sales[Amount] > 1000)
)

Cuando se usa FILTER en una tabla con millones de filas, el motor crea un contexto de iteración de fila por fila. Si la lógica de filtro solo implica una sola columna, a menudo puede reemplazar por FILTER una expresión booleana más sencilla:

-- More efficient: column filter
High Value Sales =
CALCULATE(
    [Total Sales],
    Sales[Amount] > 1000
)

KEEPFILTERS sirve para un propósito diferente. Conserva el contexto de filtro existente en lugar de reemplazarlo. Utilice KEEPFILTERS cuando desee añadir una condición de filtro sin anular lo que ya han aplicado los segmentadores u otros elementos visuales:

Online Sales =
CALCULATE(
    [Total Sales],
    KEEPFILTERS(Sales[Channel] = "Online")
)

La diferencia de rendimiento entre FILTER y los predicados de columna directa es más notable en tablas grandes. Como regla general, evite FILTER en tablas completas cuando un predicado de nivel de columna logre el mismo resultado.

Gestión de costos de la función de iterador

Funciones de iterador como SUMX, AVERAGEX, MAXX, y COUNTX evalúan una expresión para cada fila de una tabla y luego agregan los resultados. Son potentes y a veces necesarios, pero tienen un coste: especialmente en tablas grandes.

-- Iterates every row in the Sales table
Weighted Average Price =
SUMX(
    Sales,
    Sales[Quantity] * Sales[UnitPrice]
) / SUM(Sales[Quantity])

Esta fórmula funciona correctamente, pero si la tabla Sales tiene 50 millones de filas, SUMX evalúa la multiplicación de cada fila. Cuando exista una expresión no iterador equivalente, úsela:

-- Non-iterator equivalent (if LineTotal is a column)
Total Revenue = SUM(Sales[LineTotal])

Las funciones de iterador no son intrínsecamente malas. Son la opción adecuada cuando se necesita lógica de cálculo de nivel de fila que no se puede expresar con una agregación simple. La clave está en comprender el coste: el rendimiento del iterador varía con el tamaño de la tabla, por lo que un iterador de más de 1000 filas está bien, pero el mismo iterador de más de 100 millones de filas puede convertirse en un cuello de botella.

Evitar patrones costosos

Se sabe que ciertos patrones DAX provocan problemas de rendimiento. Reconocerlas ayuda a escribir mejores fórmulas desde el principio.

COUNTROWS(FILTER(...)) en tablas grandes. Este patrón itera una tabla completa para contar filas que coinciden con una condición. Reemplácela por CALCULATE y COUNTROWS por un argumento de filtro:

-- Expensive
Large Orders = COUNTROWS(FILTER(Sales, Sales[Amount] > 1000))

-- Better
Large Orders = CALCULATE(COUNTROWS(Sales), Sales[Amount] > 1000)

CALCULATE anidado con filtros complejos. En cada instancia de CALCULATE, se crea una nueva transición de contexto de filtro. Las fórmulas profundamente anidadas con varios cambios de contexto son difíciles de optimizar. Simplifique mediante la separación de medidas complejas en medidas de componente más pequeñas que cada una controle una única modificación de filtro.

Mezcla de niveles de agregación. Las medidas que combinan datos en distintos niveles de granularidad (por ejemplo, comparando el valor de una sola fila con un total de nivel de tabla) requieren transiciones de contexto que pueden ser costosas. Use variables para evaluar el total una vez y reutilizarlo:

Pct of Total =
VAR TotalSales = CALCULATE([Total Sales], REMOVEFILTERS())
RETURN
    DIVIDE([Total Sales], TotalSales)

Mover cálculos a la capa de datos

Si una medida DAX calcula el mismo resultado en cada actualización de consulta y los datos subyacentes no cambian entre actualizaciones, considere la posibilidad de materializar ese cálculo en la capa de datos en su lugar.

Por ejemplo, una columna calculada que concatena los nombres y apellidos se ejecuta durante la actualización de datos y almacena el resultado. Pero la misma lógica que una medida se ejecutaría en cada consulta. Para las transformaciones estáticas como esta, tiene dos opciones:

  • Columnas calculadas de Power Query: definir la transformación en M durante la carga de datos. Estas columnas se comprimen de forma más eficaz que las columnas calculadas de DAX porque el motor VertiPaq puede optimizar el almacenamiento durante la carga.
  • Cálculos de nivel de origen: si el origen de datos es una base de datos SQL, agregue el cálculo a la vista o consulta SQL. Esto usa las funcionalidades de optimización del motor de base de datos.

Reserve las columnas calculadas de DAX para escenarios que requieran funciones específicas de DAX, como la evaluación de medidas o el uso de funciones de inteligencia temporal que dependan de las relaciones del modelo semántico.

Importante

Trasladar los cálculos a una fase anterior no solo mejora el rendimiento de las consultas, sino que también reduce los tiempos de actualización de los datos, ya que las columnas calculadas con DAX se evalúan una vez que todas las tablas de Power Query han terminado de cargarse.

Considera la experiencia de IA

El rendimiento de DAX afecta directamente a las experiencias con tecnología de inteligencia artificial. En Microsoft Fabric, los agentes de datos de IQ y Copilot chat consultan el modelo semántico mediante la generación de consultas DAX en segundo plano. Una medida que tarda cinco segundos en un usuario humano tarda los mismos cinco segundos en Copilot: y las interacciones de IA suelen tener umbrales de tiempo de espera más estrictos que los informes interactivos.

La optimización de DAX no se trata solo de informes más rápidos. Se trata de hacer que los datos respondan lo suficiente para admitir consultas de lenguaje natural, agentes automatizados y análisis en tiempo real.