De una consulta lenta a una hipótesis comprobable

Una consulta lenta es un síntoma. Para convertirlo en una investigación útil hay que conservar la consulta, reunir evidencia y escribir una explicación que pueda fallar. La primera señal del plan no es todavía la causa.

Conservar la consulta que produjo el síntoma

La consulta resume los pedidos de un día por estado:

DECLARE @FechaObjetivo DATE = '2025-12-30';

SET STATISTICS IO ON;
SET STATISTICS TIME ON;

SELECT
    p.Estado,
    COUNT_BIG(*) AS Pedidos,
    SUM(p.Total) AS ImporteTotal
FROM dbo.PedidoLaboratorio AS p
WHERE YEAR(p.FechaPedido) = YEAR(@FechaObjetivo)
  AND MONTH(p.FechaPedido) = MONTH(@FechaObjetivo)
  AND DAY(p.FechaPedido) = DAY(@FechaObjetivo)
GROUP BY p.Estado
ORDER BY ImporteTotal DESC;

SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;

Activa Include Actual Execution Plan con Ctrl+M antes de ejecutarla. Conserva juntas las pestañas Results, Messages y Execution plan: cada una responde una pregunta distinta.

  • Results: qué devolvió la consulta.
  • Messages: cuánto trabajo de lectura y tiempo registró esa ejecución.
  • Execution plan: qué operadores ejecutó SQL Server y cuántas filas circularon por ellos.

Confirmar primero el resultado

Resultado agrupado por estado para la fecha elegida

  1. 1
    Resultado de referencia

    La fecha devuelve 68 pedidos enviados y 68 pendientes. Estos valores deben mantenerse en cualquier alternativa equivalente.

El resultado correcto no demuestra que el acceso sea eficiente. Sí establece una condición que ninguna propuesta puede romper: una variante que cambia cantidades o importes resuelve otra consulta.

Reunir evidencia del trabajo

Mensajes de SSMS con las lecturas lógicas de la consulta

  1. 1
    Trabajo de lectura

    PedidoLaboratorio registra 482 lecturas lógicas para esta ejecución.

Una lectura lógica es una página de 8 KB solicitada desde la caché de datos. No representa una fila y no implica por sí sola una lectura física. El valor sirve como línea base de esta consulta y estos parámetros.

La misma ejecución registró 32 ms de CPU y 76 ms transcurridos. Un solo tiempo no forma una tendencia: puede cambiar por caché, compilación, actividad concurrente y resolución del reloj. Por eso la hipótesis se apoya también en lecturas, filas y operadores.

Recorrer el plan y elegir una señal

Plan real con Index Scan, Hash Match Aggregate y Sort

  1. 1
    Acceso a los datos

    El árbol comienza con un Index Scan sobre el índice cuya clave es FechaPedido.

El árbol también contiene un Hash Match (Aggregate) para formar los grupos y un Sort para ordenar los importes. Esos operadores responden a lo que pide la consulta. La señal elegida para investigar es el acceso inicial porque la consulta filtra por la misma columna que encabeza el índice y, aun así, el plan la recorre como un Scan.

Un Index Scan recorre páginas de un índice y evalúa las filas que encuentra. No es un error por definición: puede ser la opción adecuada cuando se necesita una parte amplia de la estructura. Aquí interesa porque la fecha solicitada representa una fracción pequeña de la tabla.

Abrir las propiedades antes de explicar

Selecciona el Index Scan y presiona F4. Las propiedades del operador separan las filas que examinó de las que entregó al siguiente nodo.

Propiedades del Index Scan con filas entregadas y filas leídas

  1. 1
    Filas entregadas

    Actual Number of Rows for All Executions = 136: son las filas que cumplen el día solicitado.

  2. 2
    Filas examinadas

    Actual Number of Rows Read = 100000: el operador revisó toda la estructura antes de entregar esas 136 filas.

La diferencia no prueba por sí sola por qué ocurrió el recorrido. Sí delimita el problema: el operador lee muchas más filas de las que aporta al resto del plan.

Detalle del predicado del Index Scan en SSMS

  1. 1
    Condición aplicada durante el recorrido

    SSMS muestra tres comparaciones con datepart, correspondientes a YEAR, MONTH y DAY.

El predicado es la condición que decide qué filas atraviesan el operador. Aquí transforma FechaPedido antes de compararla; la captura conserva la ventana real de SSMS y sustituye únicamente el nombre interno de la base.

Separar observación e hipótesis

Las observaciones pueden verificarse directamente:

  • el resultado contiene 136 pedidos, repartidos en 68 y 68;
  • la consulta registra 482 lecturas lógicas;
  • el acceso es un Index Scan;
  • el operador lee 100,000 filas y entrega 136;
  • el predicado aplica funciones a FechaPedido.

La explicación provisional es distinta:

Aplicar YEAR, MONTH y DAY sobre la columna impide que SQL Server convierta la condición en un intervalo directo sobre la clave FechaPedido; por eso recorre el índice y evalúa cada fila.

Esta explicación es una hipótesis, no una causa confirmada. Para que sea comprobable necesita una predicción y una forma de refutarla.

Escribir una predicción que pueda fallar

La alternativa candidata expresa el mismo día como un intervalo semiabierto:

WHERE p.FechaPedido >= @FechaObjetivo
  AND p.FechaPedido < DATEADD(DAY, 1, @FechaObjetivo)

Un intervalo semiabierto incluye el límite inicial y excluye el siguiente. Así conserva cualquier hora del día elegido sin depender de que FechaPedido termine a medianoche.

La predicción es concreta: si las funciones sobre la columna explican el recorrido, el intervalo debe conservar el resultado y reducir las filas y páginas leídas mediante un acceso más directo. La hipótesis pierde apoyo si el resultado cambia, si el operador continúa leyendo una proporción semejante de la tabla o si la reducción no se repite con otras fechas.

Definir la prueba sin ejecutarla todavía

La comparación de la siguiente entrada conservará:

  • la misma proyección, agrupación y ordenamiento;
  • las mismas fechas para ambas variantes;
  • el plan real, SET STATISTICS IO y SET STATISTICS TIME;
  • una ejecución de calentamiento y varias ejecuciones medidas;
  • resultados, lecturas lógicas, filas leídas, plan, CPU y duración;
  • el mismo estado de índices y datos durante cada par.

Solo cambiará la forma del predicado. Esa restricción permite relacionar la diferencia observada con una modificación concreta, sin confundirla con otro índice, otros datos o una consulta distinta.

Siguiente comprobación razonable

La siguiente entrada ejecutará la prueba con varias fechas y comparará las distribuciones observadas. El objetivo no será ganar una sola ejecución, sino decidir si la evidencia sostiene la hipótesis sin cambiar el resultado.

Relacionado con

Índice del recorrido

Planes de ejecución

30 artículos de Planes de ejecución
  1. 01Para qué sirve un plan de ejecución
  2. 02Plan estimado, plan real y estadísticas en vivo
  3. 03Cómo se organiza el árbol de un plan de ejecución
  4. 04Flechas, propiedades y número de ejecuciones
  5. 05Mapa de operadores: qué papel cumple cada nodo
  6. 06Filas estimadas y filas reales
  7. 07Index Seek, Index Scan y Table Scan
  8. 08Key Lookup y trabajo repetido
  9. 09Filter, Compute Scalar y Top
  10. 10Nested Loops, Hash Match y Merge Join
  11. 11Sort, Segment y Sequence Project
  12. 12Stream Aggregate y Hash Match (Aggregate)
  13. 13Spool: materialización y reutilización
  14. 14Parallelism: distribuir, repartir y reunir filas
  15. 15Advertencias del plan que requieren investigación
  16. 16Acciones disponibles en el plan gráfico de SSMS
  17. 17Cómo se organiza el XML Showplan
  18. 18Recorrer Analyze Actual Execution Plan
  19. 19Lecturas con SET STATISTICS IO
  20. 20CPU y duración con SET STATISTICS TIME
  21. 21Construir una línea base repetible
  22. 22Guardar y comparar dos planes
  23. 23Historial y regresiones con Query Store
  24. 24Convertir un escenario de Analyze Actual Execution Plan en una hipótesis
  25. 25Leer una sugerencia Missing Index en el plan
  26. 26Entender el CREATE INDEX generado por SSMS
  27. 27Decidir, crear y comprobar un índice
  28. 28De una consulta lenta a una hipótesis comprobable
  29. 29Evaluar un cambio sin optimizar solo una ejecución
  30. 30Análisis guiado de una consulta empresarial reproducible