SQL Server

Execution plans en SQL Server: cómo empezar a leerlos

El plan de ejecución dice por qué una consulta es lenta si sabes dónde mirar: qué operadores revisar primero, qué advertencias importan y cuáles ignorar.

Por Leyton Manuel Espitia Diaz 6 min de lectura

Fragmento de SQL que activa SET STATISTICS IO y TIME para analizar una consulta

La primera vez que se abre un plan de ejecución, parece un diagrama de cuarenta cajas con flechas de distintos grosores y porcentajes que no suman lo que uno espera. Es fácil cerrarlo y volver a probar índices al azar.

No hace falta entender cada operador para sacarle provecho. Con cinco o seis cosas que revisar en orden, el plan responde la pregunta que importa: dónde se va el tiempo y por qué.

Estimado o real

SQL Server ofrece dos versiones del plan:

  • Plan estimado (Ctrl+L en SSMS): lo que el optimizador piensa hacer, sin ejecutar la consulta.
  • Plan real (Ctrl+M y luego ejecutar): lo que hizo, con las filas que realmente pasaron por cada operador.

Para diagnosticar, casi siempre quieres el real. La diferencia entre lo estimado y lo real es una de las pistas más valiosas del plan.

Junto con el plan, activa las estadísticas de lectura y tiempo:

SET STATISTICS IO, TIME ON;

SELECT c.Name, SUM(o.Total) AS Total
FROM Sales.Orders AS o
JOIN Sales.Customers AS c ON c.CustomerId = o.CustomerId
WHERE o.CreatedAt >= '2026-07-01'
GROUP BY c.Name;

En la pestaña Messages aparece algo así:

Table 'Orders'. Scan count 1, logical reads 48213, ...
Table 'Customers'. Scan count 0, logical reads 1840, ...
 SQL Server Execution Times: CPU time = 890 ms, elapsed time = 1204 ms.

Las lecturas lógicas (páginas de 8 KB leídas desde memoria) son la métrica más estable para comparar antes y después de un cambio. El tiempo varía según la carga del servidor; las lecturas no.

Cómo se lee: de derecha a izquierda

Los datos fluyen de derecha a izquierda. Los operadores de la derecha leen las tablas; los de la izquierda combinan, agregan y entregan el resultado. El grosor de las flechas indica cuántas filas pasan.

Primera revisión: busca las flechas gruesas. Si una flecha lleva 2 millones de filas hacia un operador que termina devolviendo 20, ahí se está haciendo trabajo de más.

Los operadores que más importan

Operador Qué significa Cuándo preocuparse
Index Seek Va directo a las filas que necesita Casi nunca
Index Scan / Clustered Index Scan Recorre el índice o la tabla completa En tablas grandes, cuando esperabas pocas filas
Key Lookup Va a la tabla por columnas que faltan en el índice Cuando se ejecuta miles de veces
Nested Loops Por cada fila de arriba, busca en la de abajo Con muchas filas en la entrada superior
Hash Match Construye una tabla hash en memoria Si derrama a tempdb
Sort Ordena en memoria Si derrama a tempdb o procesa millones de filas

Un scan no es malo por definición. En una tabla de 500 filas, o cuando la consulta realmente necesita casi toda la tabla, es la opción más eficiente. Es un problema cuando aparece donde debería haber pocas filas.

Estimadas contra reales

Pasa el cursor sobre un operador y compara:

  • Estimated Number of Rows: 12
  • Actual Number of Rows: 380.000

Cuando la diferencia es de órdenes de magnitud, el optimizador eligió el plan con información equivocada. Eligió Nested Loops pensando en 12 filas y le tocaron 380.000. Las causas habituales:

  • Estadísticas desactualizadas en una tabla que creció mucho.
  • Predicados que el optimizador no puede estimar: funciones sobre columnas, variables locales en lugar de parámetros, tablas variables.
  • Parameter sniffing: el plan se compiló con un valor muy distinto al actual.

Corregir la estimación suele arreglar el plan completo, sin tocar índices.

Las advertencias que sí importan

Un triángulo amarillo sobre un operador es una advertencia. Las más útiles:

  • CONVERT_IMPLICIT: un tipo de dato no coincide (por ejemplo nvarchar contra una columna varchar). Puede impedir un seek.
  • Spill to tempdb: un Sort o Hash Match no tuvo memoria suficiente y escribió en disco. Suele venir de una estimación de filas muy baja.
  • No join predicate: un JOIN sin condición. Casi siempre es un error en la consulta.

El costo en porcentaje: úsalo con cuidado

Cada operador muestra un porcentaje del costo total. Ese porcentaje es estimado, incluso en el plan real. Si las estimaciones están mal, los porcentajes también. Un operador con "2 %" puede ser el que realmente consume el tiempo.

Por eso, en lugar de ir directo al porcentaje más alto, empieza por las flechas gruesas y por las diferencias entre estimadas y reales.

Y el "missing index" verde

A veces el plan muestra en verde: Missing Index (Impact 94%). Es una pista útil, pero:

  • No sabe qué otros índices tienes; puede sugerir uno casi idéntico a uno existente.
  • No considera el costo que el índice agregará a las escrituras.
  • Sugiere las columnas en un orden que no siempre es el mejor.

Tómalo como punto de partida y revísalo contra los índices que ya tiene la tabla (lo vimos en índices en SQL Server).

Un orden práctico de revisión

  1. Ejecuta con el plan real y STATISTICS IO.
  2. Identifica la tabla con más lecturas lógicas.
  3. En el plan, busca la flecha más gruesa que sale de esa tabla.
  4. Compara estimadas contra reales en ese operador.
  5. Revisa las advertencias.
  6. Solo entonces piensa en índices o en reescribir la consulta.

El trade-off de optimizar con el plan

El plan de una ejecución describe esa ejecución, con esos parámetros. Una consulta afinada para un cliente con 10 pedidos puede empeorar para uno con 100.000. Prueba los cambios con valores representativos de los extremos, no solo con el que te reportaron.

Cuándo no hace falta abrir el plan

Si sys.dm_exec_requests muestra que la consulta está bloqueada por otra sesión, el plan no te va a decir nada útil: la consulta no está trabajando, está esperando. Revisa primero si está trabajando o esperando, como explicamos en por qué una consulta SQL puede ser lenta.

Conclusión

No necesitas conocer los más de cien operadores que existen. Lecturas lógicas, flechas gruesas, estimadas contra reales y advertencias: con eso se resuelve la mayoría de los casos. El resto se aprende cuando aparece.

Leyton Manuel Espitia Diaz

Escrito por

Leyton Manuel Espitia Diaz

CEO y fundador de Nakanaba

Desarrollador de software y fundador de Nakanaba. Lidera la arquitectura de Vitrin: backend en .NET, aplicaciones en Angular y .NET MAUI, SQL Server y servicios en Cloudflare.

¿Tu base de datos se volvió lenta?

Revisamos consultas, índices y modelo de datos para que tu sistema responda rápido aunque los datos sigan creciendo.

Ver desarrollo a medida