Puede aproximar lo que ve en Monitor de rendimiento y Monitor de actividad SQL Compilations/sec
y Batch Requests/sec
, mientras ejecuta algunos lotes en una ventana de consulta separada como prueba, como se detalla a continuación.
Ventana de consulta 1:
DECLARE @t1 datetime;
DECLARE @t2 datetime;
DECLARE @CompVal1 int;
DECLARE @CompVal2 int;
DECLARE @ReCompVal1 int;
DECLARE @ReCompVal2 int;
DECLARE @BatchVal1 int;
DECLARE @BatchVal2 int;
DECLARE @ElapsedMS decimal(10,2);
SELECT @t1 = GETDATE()
, @CompVal1 = (
SELECT spi.cntr_value
FROM sys.sysperfinfo spi
WHERE spi.counter_name = 'SQL Compilations/sec '
)
, @ReCompVal1 = (
SELECT spi.cntr_value
FROM sys.sysperfinfo spi
WHERE spi.counter_name = 'SQL Re-Compilations/sec '
)
, @BatchVal1 = (
SELECT spi.cntr_value
FROM sys.sysperfinfo spi
WHERE spi.counter_name = 'Batch Requests/sec '
);
WAITFOR DELAY '00:00:10.000';
SELECT @t2 = GETDATE()
, @CompVal2 = (
SELECT spi.cntr_value
FROM sys.sysperfinfo spi
WHERE spi.counter_name = 'SQL Compilations/sec '
)
, @ReCompVal2 = (
SELECT spi.cntr_value
FROM sys.sysperfinfo spi
WHERE spi.counter_name = 'SQL Re-Compilations/sec '
)
, @BatchVal2 = (
SELECT spi.cntr_value
FROM sys.sysperfinfo spi
WHERE spi.counter_name = 'Batch Requests/sec '
);
SET @ElapsedMS = DATEDIFF(MILLISECOND, @t1, @t2);
SELECT ElapsedTimeMS = @ElapsedMS
, [SQL Compilations/sec] = (@CompVal2 - @CompVal1) / @ElapsedMS * 1000
, [SQL Recompilations/sec] = (@ReCompVal2 - @ReCompVal1) / @ElapsedMS * 1000
, [Batch Requests/sec] = (@BatchVal2 - @BatchVal1) / @ElapsedMS * 1000;
En la Ventana de consulta 2, ejecute lo siguiente mientras se ejecuta el código anterior. El código simplemente ejecuta 100 lotes T-SQL:
EXEC sys.sp_executesql N'SELECT TOP(1) o.name FROM sys.objects o;';
GO 100
Si vuelve a la Ventana de consulta 1, verá algo como esto:
╔═══════════════╦══════════════════════╦══════════ ══════════════╦════════════════════╗
║ ElapsedTimeMS ║ Compilaciones SQL / seg ║ Recompilaciones SQL / seg ║ Solicitudes por lotes / seg ║
╠═══════════════╬══════════════════════╬══════════ ══════════════╬════════════════════╣
║ 10020.00 ║ 10.07984031000 ║ 0.00000000000 ║ 10.07984031000 ║
╚═══════════════╩══════════════════════╩══════════ ══════════════╩════════════════════╝
Si miramos esta consulta:
SELECT dest.text
, deqs.execution_count
FROM sys.dm_exec_query_stats deqs
CROSS APPLY sys.dm_exec_sql_text(deqs.plan_handle) dest
WHERE dest.text LIKE 'SELECT TOP(1)%'
Podemos confirmar que hubo 100 ejecuciones de la consulta de prueba.
En los resultados anteriores, puede ver que estamos obteniendo compilaciones cada vezsp_executesql
que se ejecuta la declaración. El plan para eso ciertamente se está almacenando en caché, pero vemos una compilación para ello; ¿lo que da?
Los documentos de Microsoft dicen esto sobre sp_executesql
:
sp_executesql tiene el mismo comportamiento que EXECUTE con respecto a los lotes, el alcance de los nombres y el contexto de la base de datos. La instrucción o lote de Transact-SQL en el parámetro sp_executesql @stmt no se compila hasta que se ejecuta la instrucción sp_executesql. Los contenidos de @stmt se compilan y ejecutan como un plan de ejecución separado del plan de ejecución del lote que se llama sp_executesql.
Por lo tanto, sp_executesql
sí se está recopilando cada vez que se ejecuta, incluso si el plan para el texto de comando ya está en la caché del plan. @PaulWhite muestra en su respuesta que la mayoría de las llamadas a sp_executesql no se almacenan en caché.