Leer un histograma: DBCC SHOW_STATISTICS paso a paso
En el mundo del Business Intelligence, solemos culpar a la complejidad de las medidas DAX o al exceso de transformaciones en Power Query cuando un informe va lento. Sin embargo, en arquitecturas donde SQL Server actúa como origen, el verdadero cuello de botella suele estar un nivel por debajo: en el Optimizador de Consultas. El optimizador es el cerebro que decide cómo acceder a los datos, y su capacidad de decisión depende totalmente de la calidad de las estadísticas.
Las estadísticas no son más que objetos de metadatos que describen la distribución de los valores en una columna o índice. Cuando lanzas una consulta, el motor de SQL estima cuántas filas van a cumplir tus criterios de filtrado (Cardinality Estimation). Si esa estimación falla, el motor elegirá un plan de ejecución ineficiente, como usar un Index Scan en lugar de un Index Seek, o elegir un Hash Join cuando un Nested Loop sería óptimo. Para entender por qué el motor toma estas decisiones, necesitamos abrir la caja negra mediante DBCC SHOW_STATISTICS.
Como consultores, nuestro trabajo no es solo escribir código, sino diagnosticar. Antes de lanzarte a reescribir una vista, debes comprobar qué es lo que SQL Server «cree» que hay en tus tablas. Un error común es ignorar que SQL Server solo utiliza un máximo de 200 pasos para representar la distribución de millones de filas, lo que introduce errores de redondeo y estimación que pueden arruinar el rendimiento de tus procesos de carga en un Data Warehouse.
Anatomía de DBCC SHOW_STATISTICS
Para visualizar las estadísticas de un índice o de una columna específica, utilizamos el comando DBCC SHOW_STATISTICS. Este comando nos devuelve tres conjuntos de resultados o secciones fundamentales: el encabezado (Header), el vector de densidad (Density Vector) y el histograma (Histogram).
-- Sintaxis básica para una tabla de ventas
DBCC SHOW_STATISTICS ('Fact.Ventas', 'IX_Ventas_FechaPedido');
GOLa primera sección, el Header, nos indica cuándo se actualizaron las estadísticas por última vez y cuántas filas se analizaron. Es vital fijarse en la columna Rows Sampled. Si tienes una tabla de 100 millones de filas y las estadísticas se crearon con un muestreo del 1%, es muy probable que los valores atípicos (outliers) no estén bien representados. En proyectos de retail con alta estacionalidad, esto es crítico.
La segunda sección, el Density Vector, mide la selectividad de las columnas. Un valor de densidad bajo indica que la columna tiene muchos valores únicos (alta cardinalidad), lo cual es ideal para los índices. Pero la joya de la corona es la tercera sección: el Histograma.
Las 4 columnas clave del Histograma
El histograma divide los datos en cubos o escalones (steps). Cada fila del histograma representa un rango de valores. Para interpretarlo, debemos entender qué significa cada una de sus columnas:
- RANGE_HI_KEY: Es el límite superior del escalón. Es un valor real extraído de tu columna.
- RANGE_ROWS: El número de filas cuyo valor cae dentro del rango, excluyendo el valor del límite superior (RANGE_HI_KEY).
- EQ_ROWS: El número de filas cuyo valor es exactamente igual al
RANGE_HI_KEY. - DISTINCT_RANGE_ROWS: Cuántos valores distintos existen dentro del rango, sin contar el límite superior.
- AVG_RANGE_ROWS: El promedio de filas por cada valor distinto dentro del rango. Se calcula como
RANGE_ROWS / DISTINCT_RANGE_ROWS.
Cuando el optimizador busca un valor exacto que coincide con un RANGE_HI_KEY, utiliza EQ_ROWS para su estimación. Si buscas un valor que cae entre dos escalones, el optimizador utiliza AVG_RANGE_ROWS. Aquí es donde empiezan los problemas si tus datos no son uniformes.
Cómo el optimizador estima según el filtro
Entender la lógica de estimación es fundamental para depurar planes de ejecución en SQL Server. Supongamos una tabla de Ventas donde tenemos un escalón con RANGE_HI_KEY en ‘2023-12-31’ y el anterior estaba en ‘2023-12-01’.
Si lanzas una consulta filtrando por WHERE FechaPedido = '2023-12-15', el optimizador verá que esa fecha no es un hito (no es un RANGE_HI_KEY), por lo que mirará el valor de AVG_RANGE_ROWS del escalón de diciembre. Si ese valor es 500, estimará que la consulta devolverá 500 filas. Si en la realidad ese día hubo una promoción especial y se vendieron 50.000 unidades, la estimación fallará por un factor de 100, y el plan de ejecución será desastroso.
Detección de valores sesgados (Skewed Data)
El sesgo de datos ocurre cuando ciertos valores tienen una frecuencia desproporcionadamente alta. En un modelo de contexto de evaluación en Power BI, esto se traduce en visuales que tardan una eternidad en cargar porque el motor de base de datos subestima la carga de trabajo.
Para detectar sesgos, busca filas en el histograma donde EQ_ROWS sea órdenes de magnitud superior a AVG_RANGE_ROWS. Esto indica que ese valor específico es un «punto caliente». Si tus consultas suelen filtrar por esos valores, necesitas asegurarte de que las estadísticas estén actualizadas con un FULLSCAN para que esos valores se conviertan en sus propios RANGE_HI_KEY.
| Escenario | Síntoma en Histograma | Impacto en Rendimiento |
|---|---|---|
| Datos Uniformes | AVG_RANGE_ROWS constante | Planes estables y predecibles. |
| Sesgo de Datos | EQ_ROWS >> AVG_RANGE_ROWS | Riesgo de Parameter Sniffing y derrames a TempDB. |
| Estadísticas Obsoletas | Actual vs Estimated discrepante | Escaneos de tabla innecesarios y uso excesivo de CPU. |
| Bajo Muestreo | DISTINCT_RANGE_ROWS erróneo | El optimizador ignora la selectividad real de la columna. |
Trade-offs: ¿Muestreo automático o Full Scan?
Por defecto, SQL Server actualiza las estadísticas automáticamente cuando se alcanza un umbral de modificaciones (basado en una fórmula que ha evolucionado desde SQL Server 2016). Sin embargo, este proceso automático utiliza un muestreo (sampling). Para tablas pequeñas es suficiente, pero en entornos de Data Warehouse con tablas de hechos de gran volumen, el muestreo puede ser insuficiente.
El dilema del consultor es: ¿forzamos un FULLSCAN? Hacerlo consume recursos de IO y CPU durante la carga de datos. Si tu ventana de mantenimiento es estrecha, actualizar todas las estadísticas con FULLSCAN no es viable. La decisión técnica correcta es identificar las columnas críticas (claves foráneas, fechas de hechos, estados de pedidos) y aplicar FULLSCAN solo a esas estadísticas específicas tras cada carga incremental.
Esto es especialmente relevante si estás trabajando con tecnologías modernas como Microsoft Fabric en escenarios híbridos, donde el rendimiento del Mirroring o de los Shortcuts depende de cómo el motor SQL subyacente gestione los metadatos.
Scripts útiles para el mantenimiento
No esperes a que el usuario se queje de que el informe no carga. Puedes auditar el estado de tus estadísticas con esta consulta, que te ayudará a priorizar qué histogramas necesitan una revisión profunda:
-- Identificar estadísticas que no se han actualizado en los últimos 7 días
-- y que pertenecen a tablas con más de 10.000 filas
SELECT
s.name AS NombreEstadistica,
OBJECT_NAME(s.object_id) AS Tabla,
sp.last_updated AS UltimaActualizacion,
sp.rows AS TotalFilas,
sp.rows_sampled AS FilasMuestreadas,
sp.modification_counter AS CambiosDesdeActualizacion
FROM sys.stats AS s
CROSS APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) AS sp
WHERE sp.last_updated < DATEADD(day, -7, GETDATE())
AND sp.rows > 10000
ORDER BY sp.modification_counter DESC;Si detectas que el modification_counter es muy alto en relación al total de filas, es probable que el histograma actual sea una ficción que no representa la realidad de tu negocio. Si además estás experimentando lentitud en procesos de transformación, te recomiendo revisar cuánto tarda cada paso en Power Query para descartar que el problema sea el plegado de consultas (Query Folding) generando SQL ineficiente debido a estas estadísticas pobres.
Checklist de diagnóstico de histogramas
- Fecha de actualización: ¿Las estadísticas reflejan los datos cargados en el último proceso ETL?
- Ratio de muestreo: Si
rows_sampledes mucho menor querows, considera unUPDATE STATISTICS ... WITH FULLSCAN. - Pasos del histograma: ¿Se ha llegado al límite de 200 pasos? En columnas con alta cardinalidad (IDs únicos), esto genera mucha imprecisión.
- Valores nulos: Comprueba si el primer escalón del histograma tiene una cantidad masiva de nulos (identificable por el valor
NULLenRANGE_HI_KEY). - Alineación de índices: Asegúrate de que las estadísticas existen no solo para los índices, sino para las columnas que usas frecuentemente en cláusulas
WHEREyJOIN(estadísticas de columna única).
Preguntas frecuentes
¿Por qué SQL Server solo usa 200 pasos en el histograma?
Es un equilibrio entre precisión y rendimiento. Almacenar y procesar histogramas más grandes haría que la fase de optimización de la consulta fuera demasiado lenta, compensando negativamente el ahorro de tiempo en la ejecución.
¿Es mejor actualizar estadísticas o reconstruir índices?
La reconstrucción de índices (REBUILD) actualiza las estadísticas con FULLSCAN automáticamente. Sin embargo, la reorganización (REORGANIZE) no lo hace. Si solo necesitas mejorar las estimaciones, actualizar estadísticas es mucho más rápido y menos intrusivo que reconstruir índices.
¿Cómo afectan las estadísticas al Query Folding de Power BI?
Si el optimizador de SQL Server elige un plan de ejecución pobre debido a un histograma inexacto, el tiempo de respuesta de la consulta enviada por Power BI aumentará. Esto se percibe como un fallo en Power BI, pero la solución reside en el origen de datos.
¿Qué es el Parameter Sniffing y qué tiene que ver con esto?
Ocurre cuando SQL Server genera un plan de ejecución basado en los parámetros de la primera llamada a un procedimiento. Si ese parámetro es un valor sesgado (outlier), el plan será malo para el resto de valores. Un histograma preciso ayuda a mitigar esto, pero a veces requiere el uso de pistas de consulta (query hints).




