Você pode aproximar o que vê no Monitor de desempenho e no Monitor de atividades SQL Compilations/sec
e Batch Requests/sec
, enquanto executa alguns lotes na janela de consulta separada como teste, conforme detalhado abaixo.
Janela 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;
Na janela de consulta 2, execute o seguinte enquanto o código acima estiver em execução. O código simplesmente executa 100 lotes T-SQL:
EXEC sys.sp_executesql N'SELECT TOP(1) o.name FROM sys.objects o;';
GO 100
Se você voltar para a Janela de consulta 1, verá algo assim:
╔═══════════════╦══════════════════════╦══════════ ══════════════╦════════════════════╗
║ ElapsedTimeMS Comp Compilações SQL / s ║ Recompilações SQL / s ║ Solicitações em lote / s ║
╠═══════════════╬══════════════════════╬══════════ ══════════════╬════════════════════╣
10020,00 ║ 10,07984031000 ║ 0,00000000000 ║ 10,07984031000 ║
╚═══════════════╩══════════════════════╩══════════ ══════════════╩════════════════════╝
Se olharmos para 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 houve 100 execuções da consulta de teste.
Nos resultados acima, você pode ver que estamos recebendo compilações cada vez que a sp_executesql
instrução é executada. O plano para isso certamente está sendo armazenado em cache, mas vemos uma compilação para ele; o que da?
O Microsoft Docs diz o seguinte sobre sp_executesql
:
sp_executesql tem o mesmo comportamento que EXECUTE em relação a lotes, o escopo de nomes e o contexto do banco de dados. A instrução Transact-SQL ou lote no parâmetro sp_executesql @stmt não é compilada até que a instrução sp_executesql seja executada. O conteúdo do @stmt é então compilado e executado como um plano de execução separado do plano de execução do lote chamado sp_executesql.
Portanto, sp_executesql
ele próprio está sendo compilado toda vez que é executado, mesmo que o plano para o texto do comando já esteja no cache do plano. @PaulWhite mostra em sua resposta que a maioria das chamadas para sp_executesql não é, de fato, armazenada em cache.