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);
- 1Indicador 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.
- 2Expresió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);
- 1Sin advertencia de conversión
SELECTya no muestra el triángulo y el plan no registraPlanAffectingConvert. ElIndex Scanpermanece porque alinear los tipos eliminó la conversión, pero no creó una ruta de acceso porEstado.
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);
- 1Memoria solicitada
RequestedMemoryregistra una solicitud de 8,752 KB. - 2Memoria concedida
GrantedMemorymuestra que SQL Server concedió 8,752 KB. - 3Uso máximo observado
MaxUsedMemoryregistra 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.
- 1Operador que derramó
El triángulo está sobre Sort. La advertencia corresponde a ese trabajo de ordenamiento, no a todos los operadores del plan.
- 2Evidencia 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;
- 1Última actualización
last_updatedpermite ubicar cuándo se generó la información que utiliza el optimizador. - 2Modificaciones posteriores
modification_countercuenta 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);
- 1Operador señalado
El triángulo aparece sobre
Nested Loops, el operador que combina ambas entradas. - 2Detalle de la advertencia
WarningsregistraNo Join Predicateporque la condición no relaciona columnas dep1yp2.
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
- Filas estimadas y filas reales: compara la predicción con la ejecución.
- Sort, Segment y Sequence Project: reconoce el orden que necesita una ventana.
- Stream Aggregate y Hash Match Aggregate: compara orden y memoria al formar grupos.
- Spool: distingue materialización y reutilización.
- Revisar estadísticas: inspecciona antigüedad y modificaciones.
OPTIONy query hints: limita decisiones solo después de medir.





