È possibile approssimare ciò che si vede in Performance Monitor e Activity Monitor per SQL Compilations/sece Batch Requests/sec, durante l'esecuzione di alcuni batch in una finestra di query separata come test, come descritto di seguito.
Finestra query 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;
In Query Window 2, eseguire quanto segue mentre il codice sopra è in esecuzione. Il codice esegue semplicemente 100 batch T-SQL:
EXEC sys.sp_executesql N'SELECT TOP(1) o.name FROM sys.objects o;';
GO 100
Se torni alla finestra delle query 1 vedrai qualcosa del genere:
╔═══════════════╦══════════════════════╦══════════ ══════════════╦════════════════════╗
║ ElapsedTimeMS ║ Compilazioni SQL / sec ║ Ricompilazioni SQL / sec ║ Richieste batch / sec ║
╠═══════════════╬══════════════════════╬══════════ ══════════════╬════════════════════╣
║ 10020,00 ║ 10,07984031000 ║ 0,00000000000 ║ 10,07984031000 ║
╚═══════════════╩══════════════════════╩══════════ ══════════════╩════════════════════╝
Se osserviamo questa query:
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)%'
Possiamo confermare che ci sono state 100 esecuzioni della query di test.
Nei risultati sopra, puoi vedere che stiamo ottenendo compilation ogni voltasp_executesql che viene eseguita l' istruzione. Il piano per questo è sicuramente in cache, eppure vediamo una compilation per questo; cosa dà?
I documenti Microsoft dicono questo su sp_executesql:
sp_executesql ha lo stesso comportamento di EXECUTE per quanto riguarda i batch, l'ambito dei nomi e il contesto del database. L'istruzione Transact-SQL o il batch nel parametro sp_executesql @stmt non vengono compilati fino a quando non viene eseguita l'istruzione sp_executesql. Il contenuto di @stmt viene quindi compilato ed eseguito come un piano di esecuzione separato dal piano di esecuzione del batch che ha chiamato sp_executesql.
Quindi, esso sp_executesql stesso viene compilato ogni volta che viene eseguito, anche se il piano per il testo del comando è già nella cache del piano. @PaulWhite mostra nella sua risposta che la maggior parte delle chiamate a sp_executesql non sono, di fatto, memorizzate nella cache.