Vous pouvez approximer ce que vous voyez dans l'Analyseur de performances et l'Analyseur d'activité pour SQL Compilations/sec
et Batch Requests/sec
, tout en exécutant certains lots dans une fenêtre de requête distincte comme test, comme détaillé ci-dessous.
Fenêtre de requête 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;
Dans la fenêtre de requête 2, exécutez ce qui suit pendant l'exécution du code ci-dessus. Le code exécute simplement 100 lots T-SQL:
EXEC sys.sp_executesql N'SELECT TOP(1) o.name FROM sys.objects o;';
GO 100
Si vous revenez à la fenêtre de requête 1, vous verrez quelque chose comme ceci:
╔═══════════════╦══════════════════════╦══════════ ══════════════╦════════════════════╗
║ ElapsedTimeMS ║ Compilations SQL / s ║ Recompilations SQL / s ║ Demandes de lot / s ║
╠═══════════════╬══════════════════════╬══════════ ══════════════╬════════════════════╣
20 10020,00 ║ 10,07984031000 ║ 0,0000000000000 ║ 10,07984031000 ║
╚═══════════════╩══════════════════════╩══════════ ══════════════╩════════════════════╝
Si nous regardons cette requête:
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)%'
Nous pouvons confirmer qu'il y a eu 100 exécutions de la requête de test.
Dans les résultats ci-dessus, vous pouvez voir que nous obtenons des compilations à chaquesp_executesql
exécution de l' instruction. Le plan pour cela est certainement mis en cache, mais nous voyons une compilation pour cela; ce qui donne?
Les Microsoft Docs disent ceci sur sp_executesql
:
sp_executesql a le même comportement que EXECUTE en ce qui concerne les lots, la portée des noms et le contexte de la base de données. L'instruction Transact-SQL ou le lot dans le paramètre sp_executesql @stmt n'est pas compilé tant que l'instruction sp_executesql n'est pas exécutée. Le contenu de @stmt est ensuite compilé et exécuté en tant que plan d'exécution distinct du plan d'exécution du lot appelé sp_executesql.
Ainsi, sp_executesql
lui - même est compilé à chaque exécution, même si le plan du texte de commande est déjà dans le cache du plan. @PaulWhite montre dans sa réponse que la plupart des appels à sp_executesql ne sont en fait pas mis en cache.