Database
 sql >> база данни >  >> RDS >> Database

Параметър Sniffing, Embedding и опциите RECOMPILE

Нюхене на параметри

Параметризирането на заявката насърчава повторното използване на кеширани планове за изпълнение, като по този начин се избягват ненужните компилации и се намалява броят на ad-hoc заявки в кеша на плана.

Всичко това са добри неща, при условие заявката, която се параметризира, наистина трябва да използва един и същ кеширан план за изпълнение за различни стойности на параметрите. План за изпълнение, който е ефективен за една стойност на параметъра, може да не бъде добър избор за други възможни стойности на параметрите.

Когато подслушването на параметри е разрешено (по подразбиране), SQL Server избира план за изпълнение въз основа на конкретните стойности на параметрите, които съществуват по време на компилация. Неявното допускане е, че параметризираните оператори най-често се изпълняват с най-често срещаните стойности на параметрите. Това звучи достатъчно разумно (дори очевидно) и наистина често работи добре.

Проблем може да възникне, когато възникне автоматично прекомпилиране на кеширания план. Повторна компилация може да бъде задействана по различни причини, например защото индекс, използван от кеширания план, е отпаднал (правилност повторно компилиране) или защото статистическата информация се е променила (оптимална прекомпилиране).

Каквато и да е точната причина от прекомпилирането на плана, има вероятност да се окаже нетипичен стойност се предава като параметър в момента на генериране на новия план. Това може да доведе до нов кеширан план (въз основа на подушената стойност на нетипичния параметър), който не е добър за по-голямата част от изпълненията, за които ще бъде използван повторно.

Не е лесно да се предвиди кога конкретен план за изпълнение ще бъде прекомпилиран (например, тъй като статистическите данни са се променили достатъчно), което води до ситуация, в която качествен план за многократна употреба може внезапно да бъде заменен от съвсем различен план, оптимизиран за нетипични стойности на параметрите.

Един такъв сценарий се случва, когато нетипичната стойност е силно селективна, което води до план, оптимизиран за малък брой редове. Такива планове често използват еднонишково изпълнение, вложени цикли и справки. Сериозни проблеми с производителността могат да възникнат, когато този план се използва повторно за различни стойности на параметри, които генерират много по-голям брой редове.

Деактивиране на подслушването на параметри

Подуването на параметри може да бъде деактивирано с помощта на документиран флаг за проследяване 4136. Флагът за проследяване също се поддържа за на заявка използвайте чрез QUERYTRACEON намек за заявка. И двете се прилагат от SQL Server 2005 Service Pack 4 нататък (и малко по-рано, ако прилагате кумулативни актуализации към Service Pack 3).

Започвайки със SQL Server 2016, подслушването на параметри може също да бъде деактивирано на ниво на база данни , като използвате PARAMETER_SNIFFING аргумент за ALTER DATABASE SCOPED CONFIGURATION .

Когато подслушването на параметри е деактивирано, SQL Server използва средно разпространение статистика, за да изберете план за изпълнение.

Това също звучи като разумен подход (и може да помогне да се избегне ситуацията, при която планът е оптимизиран за необичайно селективна стойност на параметъра), но също така не е перфектна стратегия:план, оптимизиран за „средна“ стойност, може да се окаже сериозно неоптимално за често срещаните стойности на параметрите.

Помислете за план за изпълнение, който съдържа оператори, които консумират памет, като сортиране и хешове. Тъй като паметта е запазена преди да започне изпълнението на заявката, параметризиран план, базиран на средните стойности на разпределението, може да се прехвърли в tempdb за общи стойности на параметри, които произвеждат повече данни, отколкото е очаквал оптимизаторът.

Резервациите на паметта обикновено не могат да нараснат по време на изпълнение на заявка, независимо от това колко свободна памет може да има сървърът. Някои приложения имат полза от изключването на подслушването на параметри (вижте тази архивна публикация от екипа за производителност на Dynamics AX за пример).

За повечето натоварвания, деактивирането на подслушването на параметри изцяло е грешно решение и дори може да бъде бедствие. Подуването на параметри е евристична оптимизация:работи по-добре от използването на средни стойности в повечето системи през повечето време.

Съвети за заявка

SQL Server предоставя набор от подсказки за заявка и други опции за настройка на поведението на подслушване на параметри:

  • OPTIMIZE FOR (@parameter = value) query hint изгражда план за многократна употреба въз основа на конкретна стойност.
  • OPTIMIZE FOR (@parameter UNKNOWN) използва средна статистика за разпределение за конкретен параметър.
  • OPTIMIZE FOR UNKNOWN използва средно разпределение за всички параметри (същият ефект като флаг за проследяване 4136).
  • WITH RECOMPILE опцията за съхранена процедура компилира нов план на процедурата за всяко изпълнение.
  • OPTION (RECOMPILE) query hint компилира нов план за отделно изявление.

Старата техника на „скриване на параметри“ (присвояване на параметри на процедурата на локални променливи и препращане към променливите вместо това) има същия ефект като посочването на OPTIMIZE FOR UNKNOWN . Тя може да бъде полезна при екземпляри по-ранни от SQL Server 2008 (OPTIMIZE FOR намек е нов за 2008 г.).

Може да се твърди, че всеки параметризираният израз трябва да бъде проверен за чувствителност към стойностите на параметрите и или оставен сам (ако поведението по подразбиране работи добре), или изрично намекнато с помощта на една от опциите по-горе.

Това рядко се прави на практика, отчасти защото извършването на изчерпателен анализ за всички възможни стойности на параметрите може да отнеме време и изисква доста напреднали умения.
Най-често такъв анализ не се извършва и проблемите с чувствителността на параметрите се решават като и когато се появят в производството.

Тази липса на предварителен анализ вероятно е една от основните причини, поради които подслушването на параметри има лоша репутация. Струва си да сте наясно с потенциала за възникване на проблеми и да извършите поне бърз анализ на изрази, които е вероятно да причинят проблеми с производителността при прекомпилиране с нетипична стойност на параметъра.

Какво е параметър?

Някои биха казали, че SELECT оператор, препращащ локална променлива, е “параметризиран израз” нещо подобно, но това не е определението, което SQL Server използва.

Разумна индикация, че даден израз използва параметри, може да се намери, като се разгледат свойствата на плана (вижте Параметри раздел в Sentry One Plan Explorer. Или щракнете върху основния възел на плана на заявката в SSMS, отворете Свойства прозорец и разгънете Списъка с параметри възел):

„Компилираната стойност“ показва снифираната стойност на параметъра, използван за компилиране на кеширания план. „Стойността по време на изпълнение“ показва стойността на параметъра за конкретното изпълнение, записано в плана.

Всяко от тези свойства може да е празно или да липсва при различни обстоятелства. Ако заявка не е параметризирана, свойствата просто ще липсват.

Само защото нищо не е просто в SQL Server, има ситуации, при които списъкът с параметри може да бъде попълнен, но изразът все още не е параметризиран. Това може да се случи, когато SQL Server опита проста параметризация (обсъдено по-късно), но реши, че опитът е „небезопасен“. В този случай маркерите за параметри ще присъстват, но планът за изпълнение всъщност не е параметризиран.

Подушването не е само за съхранени процедури

Подуването на параметри също се случва, когато партида е изрично параметризирана за повторна употреба с помощта на sp_executesql .

Например:

EXECUTE sys.sp_executesql
    N'
    SELECT
        P.ProductID,
        P.Name,
        TotalQty = SUM(TH.Quantity)
    FROM Production.Product AS P
    JOIN Production.TransactionHistory AS TH
        ON TH.ProductID = P.ProductID
    WHERE
        P.Name LIKE @NameLike
    GROUP BY
        P.ProductID,
        P.Name;
    ',
    N'@NameLike nvarchar(50)',
    @NameLike = N'K%';

Оптимизаторът избира план за изпълнение въз основа на подушената стойност на @NameLike параметър. Стойността на параметъра „K%“ се оценява да съответства на много малко редове в Product таблица, така че оптимизаторът избира стратегия за присъединяване на вложен цикъл и ключово търсене:

Повторното изпълнение на оператора със стойност на параметъра „[H-R]%“ (което ще съответства на много повече редове) използва повторно кеширания параметризиран план:

EXECUTE sys.sp_executesql
    N'
    SELECT
        P.ProductID,
        P.Name,
        TotalQty = SUM(TH.Quantity)
    FROM Production.Product AS P
    JOIN Production.TransactionHistory AS TH
        ON TH.ProductID = P.ProductID
    WHERE
        P.Name LIKE @NameLike
    GROUP BY
        P.ProductID,
        P.Name;
    ',
    N'@NameLike nvarchar(50)',
    @NameLike = N'[H-R]%';

AdventureWorks Примерната база данни е твърде малка, за да направи това бедствие в производителността, но този план със сигурност не е оптимален за стойността на втория параметър.

Можем да видим плана, който оптимизаторът би избрал, като изчисти кеша на плана и изпълни втората заявка отново:

При очакван по-голям брой съвпадения оптимизаторът определя, че хеш присъединяването и хеш агрегацията са по-добри стратегии.

T-SQL функции

Подуването на параметри се случва и с функциите на T-SQL, въпреки че начинът, по който се генерират плановете за изпълнение, може да направи това по-трудно да се види.

Има основателни причини да избягвате T-SQL скаларните функции и функциите с множество изрази като цяло, така че само за образователни цели ето една T-SQL версия на функцията с таблично стойности с множество изрази на нашата тестова заявка:

CREATE FUNCTION dbo.F
    (@NameLike nvarchar(50))
RETURNS @Result TABLE
(
    ProductID   integer NOT NULL PRIMARY KEY,
    Name        nvarchar(50) NOT NULL,
    TotalQty    integer NOT NULL
)
WITH SCHEMABINDING
AS
BEGIN
    INSERT @Result
    SELECT
        P.ProductID,
        P.Name,
        TotalQty = SUM(TH.Quantity)
    FROM Production.Product AS P
    JOIN Production.TransactionHistory AS TH
        ON TH.ProductID = P.ProductID
    WHERE
        P.Name LIKE @NameLike
    GROUP BY
        P.ProductID,
        P.Name;
 
    RETURN;
END;

Следната заявка използва функцията за показване на информация за имена на продукти, започващи с „K“:

SELECT
    Result.ProductID,
    Result.Name,
    Result.TotalQty
FROM dbo.F(N'K%') AS Result;

Виждането на подслушване на параметри с вградена функция е по-трудно, тъй като SQL Server не връща отделен план за заявка след изпълнение (действителен) за всяко извикване на функция. Функцията може да бъде извикана много пъти в рамките на един израз и потребителите няма да бъдат впечатлени, ако SSMS се опита да покаже милион планове за извикване на функции за една заявка.

В резултат на това дизайнерско решение действителният план, върнат от SQL Server за нашата тестова заявка, не е много полезен:

Въпреки това, има начини да видите снифирането на параметри в действие с вградени функции. Методът, който избрах да използвам тук, е да проверя кеша на плана:

SELECT
    DEQS.plan_generation_num,
    DEQS.execution_count,
    DEQS.last_logical_reads,
    DEQS.last_elapsed_time,
    DEQS.last_rows,
    DEQP.query_plan
FROM sys.dm_exec_query_stats AS DEQS
CROSS APPLY sys.dm_exec_sql_text(DEQS.plan_handle) AS DEST
CROSS APPLY sys.dm_exec_query_plan(DEQS.plan_handle) AS DEQP
WHERE
    DEST.objectid = OBJECT_ID(N'dbo.F', N'TF');

Този резултат показва, че планът на функциите е бил изпълнен веднъж, на цена от 201 логически четения с изминало време от 2891 микросекунди, а най-скорошното изпълнение е върнало един ред. Върнатото представяне на XML план показва, че стойността на параметъра беше подуши:

Сега стартирайте изявлението отново, с различен параметър:

SELECT
    Result.ProductID,
    Result.Name,
    Result.TotalQty
FROM dbo.F(N'[H-R]%') AS Result;

Планът след изпълнение показва, че 306 реда са върнати от функцията:

Заявката за кеширане на плана показва, че кешираният план за изпълнение на функцията е бил използван повторно (execution_count =2):

Той също така показва много по-голям брой логически четения и по-дълго изминало време в сравнение с предишното изпълнение. Това е в съответствие с повторното използване на вложени цикли и план за търсене, но за да сме напълно сигурни, планът на функциите след изпълнение може да бъде уловен с помощта на Разширени събития или SQL Server Profiler инструмент:

Тъй като подслушването на параметри се прилага за функциите, тези модули могат да страдат от същите неочаквани промени в производителността, които обикновено се свързват със съхранените процедури.

Например, когато за първи път се позовава на функция, може да се кешира план, който не използва паралелизъм. Последващите изпълнения със стойности на параметри, които биха се възползвали от паралелизма (но да използват повторно кеширания сериен план), ще покажат неочаквано ниска производителност.

Този проблем може да бъде труден за идентифициране, тъй като SQL Server не връща отделни планове след изпълнение за извиквания на функции, както видяхме. Използване на Разширени събития или Профилър рутинното заснемане на планове след изпълнение може да бъде изключително ресурсоемко, така че често има смисъл да се използва тази техника по много целенасочен начин. Трудностите около проблемите, свързани с отстраняването на грешки, свързани с чувствителността на параметрите на функцията, означават, че е още по-полезно да се направи анализ (и да се кодира защитно), преди функцията да влезе в производство.

Подслушването на параметри работи точно по същия начин с T-SQL скаларни дефинирани от потребителя функции (освен ако не са вградени, на SQL Server 2019 нататък). Вградените функции с таблична стойност не генерират отделен план за изпълнение за всяко извикване, тъй като (както казва името) те са вградени в извикващата заявка преди компилацията.

Пазете се от Sniffed NULL

Изчистете кеша на плана и поискайте приблизителен (предварително изпълнение) план за тестовата заявка:

SELECT
    Result.ProductID,
    Result.Name,
    Result.TotalQty
FROM dbo.F(N'K%') AS Result;

Ще видите два плана за изпълнение, вторият от които е за извикването на функция:

Ограничение на подслушването на параметри с вградени функции в прогнозните планове означава, че стойността на параметъра се подслушва като NULLs (не „K%“):

Във версии на SQL Server преди 2012 г. този план (оптимизиран за NULLs параметър) се кешира за повторна употреба . Това е жалко, защото NULLs е малко вероятно да е представителна стойност на параметъра и със сигурност не е стойността, посочена в заявката.

SQL Server 2012 (и по-нова версия) не кешира планове в резултат на заявка за „приблизителен план“, въпреки че все пак ще показва функционален план, оптимизиран за NULLs стойност на параметъра по време на компилация.

Проста и принудителна параметризация

Специален T-SQL израз, съдържащ постоянни литерални стойности, може да бъде параметризиран от SQL Server, или защото заявката отговаря на изискванията за проста параметризация, или защото е активирана опцията на базата данни за принудителна параметризация (или се използва ръководство за план за същия ефект).

Изявление, параметризирано по този начин, също подлежи на подслушване на параметри. Следната заявка отговаря на условията за проста параметризация:

SELECT 
    A.AddressLine1, 
    A.City, 
    A.PostalCode 
FROM Person.Address AS A 
WHERE 
    A.AddressLine1 = N'Heidestieg Straße 8664';

Прогнозният план за изпълнение показва прогноза от 2,5 реда въз основа на стойността на снифирания параметър:

Всъщност заявката връща 7 реда (оценката на кардиналността не е перфектна, дори когато стойностите се подслушват):

В този момент може да се чудите къде е доказателството, че тази заявка е била параметризирана и получената стойност на параметъра е подушена. Изпълнете заявката втори път с различна стойност:

SELECT 
    A.AddressLine1, 
    A.City, 
    A.PostalCode 
FROM Person.Address AS A 
WHERE 
    A.AddressLine1 = N'Winter der Böck 8550';

Заявката връща един ред:

Планът за изпълнение показва, че второто изпълнение е използвало повторно параметризирания план, който е компилиран с помощта на подушена стойност:

Параметризирането и подслушването са отделни дейности

Един ad-hoc израз може да бъде параметризиран от SQL Server без стойностите на параметрите се подслушват.

За да демонстрираме, можем да използваме флаг за проследяване 4136, за да деактивираме подслушването на параметри за партида, която ще бъде параметризирана от сървъра:

DBCC FREEPROCCACHE;
DBCC TRACEON (4136);
GO
SELECT
    A.AddressLine1, 
    A.City, 
    A.PostalCode 
FROM Person.Address AS A 
WHERE
    A.AddressLine1 = N'Heidestieg Straße 8664';
GO
SELECT 
    A.AddressLine1, 
    A.City, 
    A.PostalCode 
FROM Person.Address AS A 
WHERE 
    A.AddressLine1 = N'Winter der Böck 8550';
GO
DBCC TRACEOFF (4136);

Скриптът води до изрази, които са параметризирани, но стойността на параметъра не се подслушва за целите на оценката на мощността. За да видим това, можем да проверим кеша на плана:

WITH XMLNAMESPACES
    (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
SELECT
    DECP.cacheobjtype,
    DECP.objtype,
    DECP.usecounts,
    DECP.plan_handle,
    parameterized_plan_handle =
        DEQP.query_plan.value
        (
            '(//StmtSimple)[1]/@ParameterizedPlanHandle',
            'NVARCHAR(100)'
        )
FROM sys.dm_exec_cached_plans AS DECP
CROSS APPLY sys.dm_exec_sql_text(DECP.plan_handle) AS DEST
CROSS APPLY sys.dm_exec_query_plan(DECP.plan_handle) AS DEQP
WHERE 
    DEST.[text] LIKE N'%AddressLine1%'
    AND DEST.[text] NOT LIKE N'%XMLNAMESPACES%';

Резултатите показват два записа в кеша за ad-hoc заявките, свързани с параметризирания (подготвен) план за заявка чрез манипулатора на параметризиран план.

Параметризираният план се използва два пъти:

Планът за изпълнение показва различна оценка на мощността сега, когато подслушването на параметри е деактивирано:

Сравнете оценката от 1,44571 реда с оценката за 2,5 реда, използвана, когато е активирано подслушването на параметри.

При деактивирано подушване оценката идва от информация за средната честота за AddressLine1 колона. Извлечение от DBCC SHOW_STATISTICS изходът за въпросния индекс показва как е изчислено това число:умножаването на броя на редовете в таблицата (19 614) по плътността (7.370826e-5) дава оценка за 1.44571 ред.

Странична бележка: Обикновено се смята, че само целочислени сравнения, използващи уникален индекс, могат да се квалифицират за проста параметризация. Умишлено избрах този пример (сравнение на низове, използващо неуникален индекс), за да го опровергая.

С ПРЕКОМПИЛИРАНЕ и ОПЦИЯ (ПРЕКОМПИЛИРАНЕ)

Когато се срещне проблем с чувствителността на параметрите, често срещан съвет във форумите и сайтовете за въпроси и отговори е да се „използва прекомпилиране“ (ако приемем, че другите опции за настройка, представени по-рано, са неподходящи). За съжаление, този съвет често се тълкува погрешно, като означава добавяне на WITH RECOMPILE опция към съхранената процедура.

Използване на WITH RECOMPILE ефективно ни връща към поведението на SQL Server 2000, където цялата съхранена процедура се прекомпилира при всяко изпълнение.

По-добра алтернатива , на SQL Server 2005 и по-нови, е да използвате OPTION (RECOMPILE) намек за заявка само за изявление който страда от проблема с подуването на параметри. Този намек за заявка води до прекомпилиране на проблемното изявление само. Плановете за изпълнение за други оператори в рамките на съхранената процедура се кешират и се използват повторно както обикновено.

Използване на WITH RECOMPILE също така означава, че компилираният план за съхранената процедура не се кешира. В резултат на това в DMV не се поддържа информация за ефективността като sys.dm_exec_query_stats .

Използването на подсказка за заявка вместо това означава, че компилиран план може да бъде кеширан и информацията за производителността е налична в DMV (въпреки че е ограничена до най-новото изпълнение, само за засегнатия оператор).

За екземпляри, изпълняващи най-малко SQL Server 2008 build 2746 (сервизен пакет 1 с сборна актуализация 5), като се използва OPTION (RECOMPILE) има още едносъществено предимство над WITH RECOMPILE :Само OPTION (RECOMPILE) активира Оптимизация за вграждане на параметри .

Оптимизация за вграждане на параметри

Снифирането на стойностите на параметрите позволява на оптимизатора да използва стойността на параметъра за извличане на оценки за мощността. И двете WITH RECOMPILE и OPTION (RECOMPILE) водят до планове за заявки с оценки, изчислени от действителните стойности на параметрите при всяко изпълнение.

Оптимизация за вграждане на параметри прави този процес крачка напред. Параметрите на заявката са заменени с буквални константни стойности по време на анализа на заявка.

Парсерът е способен на изненадващо сложни опростявания и последващата оптимизация на заявките може да прецизира нещата още повече. Помислете за следната съхранена процедура, която включва WITH RECOMPILE опция:

CREATE PROCEDURE dbo.P
    @NameLike nvarchar(50),
    @Sort tinyint
WITH RECOMPILE
AS
BEGIN
    SELECT TOP (5)
        ProductID,
        Name
    FROM Production.Product
    WHERE
        @NameLike IS NULL
        OR Name LIKE @NameLike
    ORDER BY
        CASE WHEN @Sort = 1 THEN ProductID ELSE NULL END ASC,
        CASE WHEN @Sort = 2 THEN ProductID ELSE NULL END DESC,
        CASE WHEN @Sort = 3 THEN Name ELSE NULL END ASC,
        CASE WHEN @Sort = 4 THEN Name ELSE NULL END DESC;
END;

Процедурата се изпълнява два пъти, със следните стойности на параметрите:

EXECUTE dbo.P
	@NameLike = N'K%',
	@Sort = 1;
GO
EXECUTE dbo.P
	@NameLike = N'[H-R]%',
	@Sort = 4;

Тъй като WITH RECOMPILE се използва, процедурата се прекомпилира напълно при всяко изпълнение. Стойностите на параметрите са подушени всеки път и се използва от оптимизатора за изчисляване на оценките за мощността.

Планът за изпълнение на първата процедура е точно правилен, като се изчислява 1 ред:

Второто изпълнение оценява 360 реда, много близо до 366, наблюдавани по време на изпълнение:

И двата плана използват една и съща обща стратегия за изпълнение:сканирайте всички редове в индекс, като приложите WHERE сказуемо предикат като остатък; изчислете CASE израз, използван в ORDER BY клауза; и извършете Сортиране на първо място върху резултата от CASE израз.

ОПЦИЯ (ПРЕКОМПИЛИРАНЕ)

Сега създайте отново съхранената процедура с помощта на OPTION (RECOMPILE) намек за заявка вместо WITH RECOMPILE :

CREATE PROCEDURE dbo.P
    @NameLike nvarchar(50),
    @Sort tinyint
AS
BEGIN
    SELECT TOP (5)
        ProductID,
        Name
    FROM Production.Product
    WHERE
        @NameLike IS NULL
        OR Name LIKE @NameLike
    ORDER BY
        CASE WHEN @Sort = 1 THEN ProductID ELSE NULL END ASC,
        CASE WHEN @Sort = 2 THEN ProductID ELSE NULL END DESC,
        CASE WHEN @Sort = 3 THEN Name ELSE NULL END ASC,
        CASE WHEN @Sort = 4 THEN Name ELSE NULL END DESC
    OPTION (RECOMPILE);
END;

Изпълнението на съхранената процедура два пъти със същите стойности на параметрите, както преди, води до драматично различни планове за изпълнение.

Това е първият план за изпълнение (с параметри, изискващи имена, започващи с "K", подредени от ProductID възходящ):

Парсерът вгражда стойностите на параметрите в текста на заявката, което води до следната междинна форма:

SELECT TOP (5)
    ProductID,
    Name
FROM Production.Product
WHERE
    'K%' IS NULL
    OR Name LIKE 'K%'
ORDER BY
    CASE WHEN 1 = 1 THEN ProductID ELSE NULL END ASC,
    CASE WHEN 1 = 2 THEN ProductID ELSE NULL END DESC,
    CASE WHEN 1 = 3 THEN Name ELSE NULL END ASC,
    CASE WHEN 1 = 4 THEN Name ELSE NULL END DESC;

След това анализаторът отива по-далеч, като премахва противоречията и напълно оценява CASE изрази. Това води до:

SELECT TOP (5)
    ProductID,
    Name
FROM Production.Product
WHERE
    Name LIKE 'K%'
ORDER BY
    ProductID ASC,
    NULL DESC,
    NULL ASC,
    NULL DESC;

Ще получите съобщение за грешка, ако се опитате да изпратите тази заявка директно към SQL Server, тъй като подреждането по константна стойност не е разрешено. Независимо от това, това е формата, създадена от анализатора. Разрешено е вътрешно, защото е възникнало в резултат на прилагане на оптимизацията за вграждане на параметри . Опростената заявка прави живота много по-лесен за оптимизатора на заявки:

Клъстерното индексно сканиране прилага LIKE предикат като остатък. Изчисляване на скалар предоставя константата NULLs стойности. Върхът връща първите 5 реда в реда, предоставен от Клъстерирания индекс (избягване на вид). В един перфектен свят оптимизаторът на заявки би премахнал и Compute Scalar който дефинира NULLs , тъй като те не се използват по време на изпълнение на заявка.

Второто изпълнение следва точно същия процес, което води до план за заявка (за имена, започващи с буквите „H“ до „R“, подредени по Name низходящ) като това:

Този план включва Неклъстерно търсене на индекси който покрива LIKE диапазон, остатъчен LIKE предикат, константата NULLs както преди, и Топ (5). Оптимизаторът на заявки избира да извърши BACKWARD сканиране на диапазона в Търсене на индекс за да избегнете сортирането отново.

Сравнете плана по-горе с този, създаден с помощта на WITH RECOMPILE , който не може да използва оптимизацията за вграждане на параметри :

Този демонстрационен пример може да е бил по-добре реализиран като серия от IF оператори в процедурата (по един за всяка комбинация от стойности на параметрите). Това би могло да осигури подобни предимства на плана за заявки, без да изисква компилация на изявление всеки път. В по-сложни сценарии, прекомпилирането на ниво оператор с вграждане на параметри, предоставено от OPTION (RECOMPILE) може да бъде изключително полезна техника за оптимизиране.

Ограничение за вграждане

Има един сценарий, при който се използва OPTION (RECOMPILE) няма да доведе до прилагане на оптимизация за вграждане на параметри. Ако изразът присвоява променлива, стойностите на параметрите не са вградени:

CREATE PROCEDURE dbo.P
    @NameLike nvarchar(50),
    @Sort tinyint
AS
BEGIN
    DECLARE
        @ProductID integer,
        @Name nvarchar(50);
 
    SELECT TOP (1)
        @ProductID = ProductID,
        @Name = Name
    FROM Production.Product
    WHERE
        @NameLike IS NULL
        OR Name LIKE @NameLike
    ORDER BY
        CASE WHEN @Sort = 1 THEN ProductID ELSE NULL END ASC,
        CASE WHEN @Sort = 2 THEN ProductID ELSE NULL END DESC,
        CASE WHEN @Sort = 3 THEN Name ELSE NULL END ASC,
        CASE WHEN @Sort = 4 THEN Name ELSE NULL END DESC
    OPTION (RECOMPILE);
END;

Тъй като SELECT операторът сега присвоява на променлива, произведените планове на заявката са същите като когато WITH RECOMPILE беше използван. Стойностите на параметрите все още се подслушват и използват от оптимизатора на заявки за оценка на мощността и OPTION (RECOMPILE) все още компилира само единичния израз, само предимството на вграждането на параметри се губи.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Броячите на Zombie PerfMon, които никога не умират!

  2. Основи на табличните изрази, част 1

  3. Как да ограничим резултатите в T-SQL

  4. Дизайн на база данни с Vertabelo

  5. Изчакайте Статистика и Съхранение на заявки