Advertencias del plan que requieren investigación

Un triángulo amarillo llama la atención sobre una condición que SQL Server pudo identificar al compilar o ejecutar una consulta. La advertencia señala dónde conviene mirar; no demuestra por sí sola qué causó una demora ni prescribe una corrección.

Antes de reaccionar, distinguimos qué detectó el motor, en qué momento pudo detectarlo y qué evidencia adicional necesitamos. Una conversión implícita pertenece a la expresión compilada; un derrame a tempdb describe algo que ocurrió durante la ejecución. No se interpretan de la misma manera.

Dónde aparece una advertencia

SSMS muestra el triángulo sobre el operador relacionado o sobre el operador raíz SELECT. Al colocar el puntero sobre ese punto encontramos una sección Warnings con el detalle disponible. También podemos seleccionar el operador y presionar F4 para revisar sus propiedades.

Para producir las capturas de esta entrada, activa Include Actual Execution Plan con Ctrl+M y ejecuta cada caso por separado. Después abre Execution plan; el triángulo no aparece en Results ni en Messages. Algunas advertencias solo se registran en un plan real porque dependen de lo ocurrido durante la ejecución.

El operador raíz representa la instrucción completa. Una advertencia colocada allí puede referirse a una expresión situada más adelante en el árbol; debemos leer el texto antes de atribuirla al icono que la contiene.

Conversión implícita

Una conversión implícita ocurre cuando SQL Server debe llevar dos valores a un tipo compatible sin que la consulta haya escrito la conversión expresamente. La precedencia de tipos decide cuál se transforma: cuando los tipos tienen distinta prioridad, SQL Server convierte el de menor precedencia al de mayor precedencia.

En esta prueba, Estado es VARCHAR(20) y la variable es NVARCHAR(20):

DECLARE @Estado NVARCHAR(20) = N'Pendiente';

SELECT COUNT_BIG(*) AS Pedidos
FROM dbo.PedidoLaboratorio AS p
WHERE p.Estado = @Estado
OPTION (RECOMPILE);

Advertencia real de conversión implícita en un plan de SSMS

  1. 1
    Indicador de advertencia

    El triángulo aparece en SELECT. Su ubicación identifica la instrucción que contiene la señal; todavía necesitamos abrir el detalle.

  2. 2
    Expresión convertida

    Warnings muestra CONVERT_IMPLICIT sobre p.Estado y señala que puede afectar una alternativa de Seek.

La observación es concreta: el plan convirtió p.Estado de VARCHAR a NVARCHAR y registró PlanAffectingConvert. La consulta leyó las 100,000 filas del índice recorrido y entregó 25,000 al agregado.

Eso todavía no demuestra que la conversión haya causado el Scan. La tabla de esta prueba no tiene un índice cuya llave comience por Estado; sin esa ruta, alinear los tipos no garantiza que aparezca un Seek. La hipótesis debe probarse con las estructuras y el volumen reales.

Alinear los tipos de la comparación

Podemos repetir la consulta con una variable que conserve el tipo de la columna:

DECLARE @Estado VARCHAR(20) = 'Pendiente';

SELECT COUNT_BIG(*) AS Pedidos
FROM dbo.PedidoLaboratorio AS p
WHERE p.Estado = @Estado
OPTION (RECOMPILE);

Plan real después de alinear los tipos de la comparación

  1. 1
    Sin advertencia de conversión

    SELECT ya no muestra el triángulo y el plan no registra PlanAffectingConvert. El Index Scan permanece porque alinear los tipos eliminó la conversión, pero no creó una ruta de acceso por Estado.

La comprobación no termina al desaparecer el triángulo: contrastamos el plan, las filas leídas y las métricas. También revisamos por qué la aplicación o el procedimiento envió otro tipo, porque corregir solo una consulta puede dejar el mismo contrato inconsistente en otras llamadas.

Concesión de memoria

Una concesión de memoria es memoria de ejecución reservada para operadores como Sort, uniones hash y agregaciones hash. El optimizador calcula la solicitud con las filas y el tamaño que estima; al ejecutar, SQL Server concede la cantidad disponible dentro de sus límites.

En el operador raíz podemos encontrar valores como:

  • Requested Memory: cantidad solicitada para ejecutar el plan;
  • Granted Memory: cantidad concedida;
  • Max Used Memory: máximo utilizado durante esa ejecución.

Una concesión grande no es automáticamente excesiva y una pequeña no es automáticamente eficiente. Debemos relacionarla con el volumen, la concurrencia, el uso observado y posibles esperas por memoria.

La consulta siguiente ordena 100,000 filas para calcular ROW_NUMBER. En la ejecución sin límite, solicitó y recibió 8,752 KB, utilizó como máximo 5,512 KB y no derramó datos:

SELECT SUM(q.Posicion) AS SumaPosiciones
FROM
(
    SELECT ROW_NUMBER() OVER
    (
        ORDER BY
            p.Total DESC,
            p.IdPedido
    ) AS Posicion
    FROM dbo.PedidoLaboratorio AS p
) AS q
OPTION (MAXDOP 1, RECOMPILE);

Propiedades reales de la concesión de memoria en SSMS

  1. 1
    Memoria solicitada

    RequestedMemory registra una solicitud de 8,752 KB.

  2. 2
    Memoria concedida

    GrantedMemory muestra que SQL Server concedió 8,752 KB.

  3. 3
    Uso máximo observado

    MaxUsedMemory registra 5,512 KB durante esta ejecución.

Esos valores describen esta ejecución en SQL Server 2019 Express. No constituyen umbrales generales para otra instancia.

Derrame a tempdb

Un derrame ocurre cuando un operador de ordenamiento o hash no conserva todo su trabajo dentro de la memoria concedida y utiliza tempdb como espacio auxiliar. tempdb es la base de datos del sistema que SQL Server emplea para resultados y estructuras temporales.

Para observarlo de forma reproducible, esta prueba limita deliberadamente la concesión de la misma consulta:

SELECT SUM(q.Posicion) AS SumaPosiciones
FROM
(
    SELECT ROW_NUMBER() OVER
    (
        ORDER BY
            p.Total DESC,
            p.IdPedido
    ) AS Posicion
    FROM dbo.PedidoLaboratorio AS p
) AS q
OPTION
(
    MAX_GRANT_PERCENT = 0.1,
    MAXDOP 1,
    RECOMPILE
);

MAX_GRANT_PERCENT se utiliza aquí únicamente para producir el caso didáctico. No se propone como corrección para una consulta real.

Advertencia real de derrame de Sort a tempdb en un plan de SSMS

  1. 1
    Operador que derramó

    El triángulo está sobre Sort. La advertencia corresponde a ese trabajo de ordenamiento, no a todos los operadores del plan.

  2. 2
    Evidencia de ejecución

    Warnings registra nivel 2, un hilo afectado, 610 páginas escritas y leídas en tempdb y una concesión utilizada de 512 KB.

El derrame aparece en el plan real porque depende de lo ocurrido durante la ejecución. Un plan estimado puede mostrar el Sort y la memoria calculada, pero no puede afirmar que esa ejecución futura escribió en tempdb.

La observación tampoco decide la corrección. Investigamos por qué la concesión no alcanzó: estimaciones, ancho de fila, volumen, límite impuesto, presión simultánea o cambios del entorno. Quitar un Sort, aumentar memoria o agregar un índice sin esa comprobación puede trasladar el costo a otra parte.

Estadísticas ausentes o desactualizadas

Las estadísticas describen la distribución de valores que el optimizador utiliza para estimar filas. Una advertencia de estadísticas ausentes indica que una estadística útil no estuvo disponible durante la compilación. Revisamos AUTO_CREATE_STATISTICS, AUTO_UPDATE_STATISTICS, permisos, estado de solo lectura y el objeto señalado.

Una estadística desactualizada no siempre genera un triángulo. Podemos encontrarla al comparar filas estimadas y reales, revisar la fecha de actualización y observar cuántas modificaciones ocurrieron desde entonces. La ausencia de una advertencia no demuestra que las estimaciones sean suficientes.

sys.stats enumera las estadísticas del objeto. sys.dm_db_stats_properties agrega la fecha de actualización, las filas registradas y el contador de modificaciones posteriores para cada una:

SELECT
    s.name,
    sp.last_updated,
    sp.rows,
    sp.modification_counter
FROM sys.stats AS s
CROSS APPLY sys.dm_db_stats_properties
(
    s.object_id,
    s.stats_id
) AS sp
WHERE s.object_id = OBJECT_ID(N'dbo.PedidoLaboratorio')
ORDER BY s.name;

Resultado real con fecha de actualización y modificaciones de las estadísticas

  1. 1
    Última actualización

    last_updated permite ubicar cuándo se generó la información que utiliza el optimizador.

  2. 2
    Modificaciones posteriores

    modification_counter cuenta cambios observados desde esa actualización. El valor se interpreta con el tamaño y la distribución del objeto, no como un umbral aislado.

Unión sin predicado

Una unión sin predicado combina dos entradas sin una condición que relacione sus filas. La condición de esta prueba sólo compara una columna de p1 consigo misma; no contiene ninguna referencia a p2 y, por tanto, no establece una relación entre ambos lados:

SELECT TOP (10)
    p1.IdPedido AS Pedido1,
    p2.IdPedido AS Pedido2
FROM dbo.PedidoLaboratorio AS p1
INNER LOOP JOIN dbo.PedidoLaboratorio AS p2
    ON p1.IdPedido = p1.IdPedido
OPTION (MAXDOP 1, RECOMPILE);

Advertencia real No Join Predicate en Nested Loops

  1. 1
    Operador señalado

    El triángulo aparece sobre Nested Loops, el operador que combina ambas entradas.

  2. 2
    Detalle de la advertencia

    Warnings registra No Join Predicate porque la condición no relaciona columnas de p1 y p2.

La advertencia puede revelar un error que multiplica filas o acompañar una combinación cartesiana deliberada. TOP (10) limita el resultado entregado en esta prueba, pero no convierte la condición en una relación válida. El texto de la consulta y el resultado esperado permiten distinguir la intención; el icono no puede hacerlo por sí solo.

Separar señal, hipótesis y comprobación

Etapa Pregunta
Señal observada ¿Qué texto, operador, valores y ejecución produjo la advertencia?
Hipótesis ¿Qué condición podría explicar esa señal y mediante qué mecanismo?
Comprobación ¿Qué comparación controlada confirmaría o descartaría esa explicación?

La investigación conserva también el resultado correcto de la consulta. Una modificación que elimina el triángulo pero cambia filas, orden, concurrencia o estabilidad no constituye una mejora válida.

Siguiente comprobación razonable

La siguiente entrada recorre las acciones disponibles en el plan gráfico de SSMS. Distinguirá las que se abren sobre un operador de las que pertenecen al lienzo completo y qué información aporta cada una, sin tratarlas como un diagnóstico automático.

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