Możesz przybliżać to, co widzisz w Monitorze wydajności i Monitorze aktywności dla SQL Compilations/sec
i Batch Requests/sec
podczas uruchamiania niektórych partii w osobnym oknie zapytania jako testu, jak opisano poniżej.
Okno zapytania 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;
W oknie zapytania 2 uruchom następujące polecenie podczas działania powyższego kodu. Kod wykonuje po prostu 100 partii T-SQL:
EXEC sys.sp_executesql N'SELECT TOP(1) o.name FROM sys.objects o;';
GO 100
Jeśli wrócisz do okna zapytania 1, zobaczysz coś takiego:
╔═══════════════╦══════════════════════╦══════════ ══════════════╦════════════════════╗
║ ElapsedTimeMS ║ Kompilacje SQL / s ║ Rekompilacje SQL / s Request Żądania wsadowe / s ║
╠═══════════════╬══════════════════════╬══════════ ══════════════╬════════════════════╣
║ 10020,00 ║ 10,07984031000 ║ 0,00000000000 ║ 10,07984031000 ║
╚═══════════════╩══════════════════════╩══════════ ══════════════╩════════════════════╝
Jeśli spojrzymy na to zapytanie:
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)%'
Możemy potwierdzić, że wykonano 100 zapytań testowych.
W powyższych wynikach widać, że otrzymujemy kompilacje za każdym razem, gdy sp_executesql
wykonywana jest instrukcja. Plan na pewno jest buforowany, ale widzimy kompilację; co daje?
W Microsoft Docs powiedzieć o sp_executesql
:
sp_executesql działa tak samo jak EXECUTE w odniesieniu do partii, zakresu nazw i kontekstu bazy danych. Instrukcja Transact-SQL lub partia w parametrze sp_executesql @stmt nie jest kompilowana, dopóki nie zostanie wykonana instrukcja sp_executesql. Zawartość @stmt jest następnie kompilowana i wykonywana jako plan wykonania odrębny od planu wykonania partii o nazwie sp_executesql.
Tak więc sp_executesql
sama kompiluje się przy każdym uruchomieniu, nawet jeśli plan tekstu polecenia jest już w pamięci podręcznej planu. @PaulWhite pokazuje w swojej odpowiedzi, że większość wywołań do sp_executesql w rzeczywistości nie jest buforowana.