Ви можете наблизити те, що ви бачите в Моніторі продуктивності та Моніторі діяльності, SQL Compilations/sec
і Batch Requests/sec
, виконуючи деякі партії, в окремому вікні запитів, як тест, як детально описано нижче.
Вікно запиту 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;
У вікні запитів 2 виконайте такі дії, поки працює вищевказаний код. Код просто виконує 100 пакетів T-SQL:
EXEC sys.sp_executesql N'SELECT TOP(1) o.name FROM sys.objects o;';
GO 100
Якщо ви повернетесь до вікна запитів 1, ви побачите щось подібне:
╔═══════════════╦══════════════════════╦══════════ ══════════════╦════════════════════╗
║ ElapsedTimeMS ║ Компіляції SQL / сек ║ Рекомпіляції SQL / сек ║ Пакетні запити / сек ║
╠═══════════════╬══════════════════════╬══════════ ══════════════╬════════════════════╣
║ 10020,00 ║ 10.07984031000 ║ 0,00000000000 ║ 10.07984031000 ║
╚═══════════════╩══════════════════════╩══════════ ══════════════╩════════════════════╝
Якщо ми подивимось на цей запит:
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)%'
Ми можемо підтвердити, що було 100 виконань тестового запиту.
У наведених вище результати, ви можете бачити , ми отримуємо компіляції кожен раз , коли в sp_executesql
виконання оператора. План для цього, безумовно, кешований, але ми бачимо компіляцію для цього; що дає?
У Docs Microsoft сказати про sp_executesql
:
sp_executesql має таку саму поведінку, що і EXECUTE щодо пакетів, обсягу імен та контексту бази даних. Оператор або пакетна команда Transact-SQL в параметрі sp_executesql @stmt не компілюється, поки не буде виконано оператор sp_executesql. Вміст @stmt потім компілюється та виконується у вигляді плану виконання окремо від плану виконання партії, що називається sp_executesql.
Отже, sp_executesql
сам складається під час кожного запуску, навіть якщо план командного тексту вже знаходиться в кеші плану. @PaulWhite у своїй відповіді показує, що більшість дзвінків до sp_executesql насправді не кешовані.