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

Проста параметризация и тривиални планове — част 3

Планове за изпълнение

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

Нека разгледаме някои примери с помощта на базата данни Stack Overflow 2010 на SQL Server 2019 CU 14, като съвместимостта на базата данни е зададена на 150.

За да започнем, ще ни трябва нов неклъстериран индекс:

CREATE INDEX [IX dbo.Users Reputation (DisplayName)] 
ON dbo.Users (Reputation) 
INCLUDE (DisplayName);

1. Приложена проста параметризация

Тази първа примерна заявка използва проста параметризация :

SELECT U.DisplayName 
FROM dbo.Users AS U 
WHERE U.Reputation = 999;

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

Прогнозни свойства за параметризиране на плана

Обърнете внимание на @1 параметърът се въвежда навсякъде с изключение на текста на заявката, показан в горната част.

действителното (след изпълнение) планът има:

Свойства за параметриране на действителен план

Забележете, че прозорецът със свойства вече е загубил ParameterizedText елемент, докато получавате информация за стойността на параметъра по време на изпълнение. Параметризираният текст на заявката вече се показва в горната част на прозореца с „@1 “ вместо „999“.

2. Проста параметризация не е приложена

Този втори примерне използвайте проста параметризация:

-- Projecting an extra column
SELECT 
    U.DisplayName, 
    U.CreationDate -- NEW
FROM dbo.Users AS U 
WHERE 
    U.Reputation = 999;

Прогнозното планът показва:

Прогнозен непараметризиран план

Този път параметърът @1 липсва в Търсене на индекс подсказка, но параметризираният текст и другите елементи на списъка с параметри са същите като преди.

Нека да разгледаме действителните план за изпълнение:

Действителен непараметризиран план

Резултатите са същите като предишните параметризирани действителни план, освен сега Търсене на индекс подсказката показва непараметризираната стойност „999“. Текстът на заявката, показан отгоре, използва @1 маркер за параметър. Прозорецът със свойства също използва @1 и показва стойността на параметъра по време на изпълнение.

Заявката не е параметризиран израз въпреки всички доказателства за противното.

3. Неуспешна настройка на параметрите

Третият ми пример също ене параметризирани от сървъра:

-- LOWER function used
SELECT 
    U.DisplayName, 
    LOWER(U.DisplayName)
FROM dbo.Users AS U 
WHERE 
    U.Reputation = 999;

Прогнозното планът е:

Неуспешно параметризиране на прогнозния план

Няма споменаване на @1 параметър навсякъде сега и Списък с параметри липсва раздел от прозореца със свойства.

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

4. Паралелен параметризиран план

Искам да ви покажа още един пример за използване на паралелизъм в плана за изпълнение. Ниската прогнозна цена на моите тестови заявки означава, че трябва да намалим прага на разходите за паралелизъм до 1:

EXECUTE sys.sp_configure
    @configname = 'cost threshold for parallelism',
    @configvalue = 1;
RECONFIGURE;

Този път примерът е малко по-сложен:

SELECT 
    U.DisplayName 
FROM dbo.Users AS U 
WHERE 
    U.Reputation >= 5 
    AND U.DisplayName > N'ZZZ' 
ORDER BY 
    U.Reputation DESC;

Прогнозното планът за изпълнение е:

Прогнозен паралелен параметризиран план

Текстът на заявката в горната част остава непараметризиран, докато всичко останало е. Сега има два маркера за параметри, @1 и @2 , тъй като проста параметризация намери две подходящи буквални стойности.

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

Действителен паралелен параметризиран план

Текстът на заявката в горната част вече е параметризиран и прозорецът със свойства съдържа стойности на параметрите по време на изпълнение. Този паралелен план (с a Сортиране оператор) определено се параметризира от сървъра с помощта на проста параметризация .

Надеждни методи

Има причини за всички показани досега поведения и още няколко. Ще се опитам да обясня много от тях в следващата част от тази поредица, когато разглеждам компилацията на планове.

Междувременно ситуацията с showplan като цяло и SSMS в частност не е идеална. Това е объркващо за хората, които работят със SQL Server през цялата си кариера. На кои маркери за параметри имате доверие и кои пренебрегвате?

Има няколко надеждни метода за определяне дали към даден конкретен оператор е приложена проста параметризация успешно или не.

Съхранение на заявки

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

Трябва да активирате функцията за съхранение на заявки за контекста на базата данни където се изпълнява операторът и OPERATION_MODE трябва да бъде настроен на READ_WRITE , което позволява на хранилището за заявки да събира активно данни.

След изпълнение на тези условия, изходът на showplan след изпълнение съдържа допълнителни атрибути, включително StatementParameterizationType . Както подсказва името, това съдържа код, описващ типа параметризация, използвана за израза.

Вижда се в прозореца със свойства на SSMS, когато е избран основният възел на план:

StatementParameterizationType

Стойностите са документирани в sys.query_store_query :

  • 0 – Няма
  • 1 – Потребител (изрично параметризиране)
  • 2 – Проста параметризация
  • 3 – Принудително параметриране

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

След като планът е кеширан, StatementParameterizationType се появява на обичайните места, включително чрез sys.dm_exec_query_plan .

Можете също така да се доверите на други места, типът параметризация на който се записва в хранилището на заявки, като например query_parameterization_type_desc колона в sys.query_store_query .

Едно важно предупреждение. Когато заявката съхранява OPERATION_MODE е настроен на READ_ONLY , StatementParameterizationType атрибутът все още се попълва в SSMS действителен планове, но винаги е нула — създава погрешно впечатление, че изявлението не е параметризирано, когато може да е било така.

Ако сте доволни да активирате хранилището на заявки, сте сигурни, че е четене-запис, и разглеждате само плановете след изпълнение в SSMS, това ще работи за вас.

Предикати за стандартен план

Текстът на заявката, показан в горната част на прозореца на графичния план за показване в SSMS, не е надежден, както показват примерите. Нито можете да разчитате на ParameterList се показва в Свойства прозорец, когато е избран основният възел на плана. Параметризиран текст атрибут, показан за приблизително само планове също не е окончателно.

Можете обаче да разчитате на свойствата, свързани с индивидуалните оператори на план. Дадените примери показват, че те присъстват в подсказките за инструменти когато задържите курсора на мишката върху оператор.

Предикат, съдържащ маркер за параметър като @1 или @2 показва параметризиран план. Операторите, които най-вероятно ще съдържат параметър, са Сканиране на индекса , Търсене в индекс и Филтър .

Предикати с маркери за параметри

Ако номерирането започва с @1 , използва проста параметризация . Принудителното параметризиране започва с @0 . Трябва да спомена, че документираната тук схема за номериране подлежи на промяна по всяко време:

Предупреждение за промяна

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

Обекти за динамично управление

Има няколко начина за запитване на кеша на плана и свързаните DMO, за да определите дали даден израз е параметризиран. Естествено, тези заявки работят само върху планове в кеша, така че изявлението трябва да е било изпълнено до завършване, кеширано и впоследствие да не е изгонено по някаква причина.

Най-директният подход е да потърсите Adhoc планирайте, като използвате точно SQL текстово съвпадение с изявлението, което представлява интерес. Adhoc планът ще бъде обвивка съдържащ ParameterizedPlanHandle ако операторът е параметризиран от сървъра. След това манипулаторът на плана се използва за намиране на Подготвен план. Adhoc планът няма да съществува, ако оптимизирането за ad hoc работни натоварвания е активирано и въпросният оператор е изпълнен само веднъж.

Този тип запитване често завършва с раздробяване на значително количество XML и сканиране на целия кеш на плана поне веднъж. Също така е лесно да сбъркате кода, не на последно място защото плановете в кеша обхващат цяла партида. Една партида може да съдържа множество оператори, всеки от които може или не може да бъде параметризиран. Не всички DMO работят с една и съща детайлност (партида или изявление), което прави лесно отстраняването.

По-долу е показан ефективен начин за изброяване на изявления, представляващи интерес, заедно с фрагменти от план само за тези отделни изявления:

SELECT
    StatementText =
        SUBSTRING(T.[text], 
            1 + (QS.statement_start_offset / 2), 
            1 + ((QS.statement_end_offset - 
                QS.statement_start_offset) / 2)),
    IsParameterized = 
        IIF(T.[text] LIKE N'(%',
            'Yes',
            'No'),
    query_plan = 
        TRY_CONVERT(xml, P.query_plan)
FROM sys.dm_exec_query_stats AS QS
CROSS APPLY sys.dm_exec_sql_text (QS.[sql_handle]) AS T
CROSS APPLY sys.dm_exec_text_query_plan (
    QS.plan_handle, 
    QS.statement_start_offset, 
    QS.statement_end_offset) AS P
WHERE 
    -- Statements of interest
    T.[text] LIKE N'%DisplayName%Users%'
    -- Exclude queries like this one
    AND T.[text] NOT LIKE N'%sys.dm%'
ORDER BY
    QS.last_execution_time ASC,
    QS.statement_start_offset ASC;

За да илюстрираме, нека стартираме една партида, съдържаща четирите примера от по-рано:

ALTER DATABASE SCOPED CONFIGURATION 
    CLEAR PROCEDURE_CACHE;
GO
-- Example 1
SELECT U.DisplayName 
FROM dbo.Users AS U 
WHERE U.Reputation = 999;
 
-- Example 2
SELECT 
    U.DisplayName, 
    U.CreationDate 
FROM dbo.Users AS U 
WHERE 
    U.Reputation = 999;
 
-- Example 3
SELECT 
    U.DisplayName, 
    LOWER(U.DisplayName)
FROM dbo.Users AS U 
WHERE 
    U.Reputation = 999;
 
-- Example 4
SELECT 
    U.DisplayName 
FROM dbo.Users AS U 
WHERE 
    U.Reputation >= 5 
    AND U.DisplayName > N'ZZZ' 
ORDER BY 
    U.Reputation DESC;
GO

Резултатът от DMO заявката е:

Изход на DMO заявка

Това потвърждава, че само примери 1 и 4 са били успешно параметризирани.

Броячи на производителност

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

Ще допълня информацията за брояча на параметризацията с данни от sys.dm_exec_query_optimizer_info DMO за предоставяне на статистически данни и за тривиални планове.

Необходима е известна грижа, за да се предотврати промяната на самите изявления, които четат информацията за брояча. Ще се справя с това, като създам няколко временни съхранени процедури:

CREATE PROCEDURE #TrivialPlans
AS
SET NOCOUNT ON;
 
SELECT
    OI.[counter],
    OI.occurrence
FROM sys.dm_exec_query_optimizer_info AS OI
WHERE
    OI.[counter] = N'trivial plan';
GO
CREATE PROCEDURE #PerfCounters
AS
SET NOCOUNT ON;
 
SELECT
    PC.[object_name],
    PC.counter_name,
    PC.cntr_value
FROM 
    sys.dm_os_performance_counters AS PC
WHERE 
    PC.counter_name LIKE N'%Param%';

След това скриптът за тестване на конкретен израз изглежда така:

ALTER DATABASE SCOPED CONFIGURATION 
    CLEAR PROCEDURE_CACHE;
GO
EXECUTE #PerfCounters;
EXECUTE #TrivialPlans;
GO
SET SHOWPLAN_XML ON;
GO
-- The statement(s) under test:
-- Example 3
SELECT 
    U.DisplayName, 
    LOWER(U.DisplayName)
FROM dbo.Users AS U 
WHERE 
    U.Reputation = 999;
GO
SET SHOWPLAN_XML OFF;
GO
EXECUTE #TrivialPlans;
EXECUTE #PerfCounters;

Коментирайте SHOWPLAN_XML пакети, за да стартирате целевия(и) израз(и) и да получите действителен планове. Оставете ги на място за оценка планове за изпълнение.

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

Резултати от теста на брояча на производителността

По-горе подчертах къде стойностите са се променили при тестване на пример 3.

Увеличението на брояча на „тривиален план“ от 1050 на 1051 показва, че е намерен тривиален план за тестовото изявление.

Броячите за проста параметризация се увеличиха с 1 както за опити, така и за неуспехи, показвайки, че SQL Server се е опитал да параметризира израза, но не успя.

Край на част 3

В следващата част от тази поредица ще обясня любопитните неща, които видяхме, като опиша как просто параметризиране и тривиални планове взаимодействат с процеса на компилация.

Ако сте променили своя разходен праг за паралелизъм за да стартирате примерите, не забравяйте да го нулирате (моят беше настроен на 50):

EXECUTE sys.sp_configure
    @configname = 'cost threshold for parallelism',
    @configvalue = 50;
RECONFIGURE;

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Видове SQL JOIN

  2. Как да получите вчерашна дата в T-SQL

  3. Salesforce SOQL от Microsoft Office

  4. Грешка ORA-65048 при промяна на потребителска парола в контейнерна база данни (CDB)

  5. SQL INSERT за начинаещи