Расчетный и фактический план запроса с вызовами функций

11

У меня есть этот запрос на сервере SQL, запрос репликации слиянием:

SELECT DISTINCT
    b.tablenick,
    b.rowguid,
    c.generation,
    sys.fn_MSgeneration_downloadonly
    (
        c.generation,
        c.tablenick
    )
FROM #belong b
LEFT OUTER JOIN dbo.MSmerge_contents c ON 
    c.tablenick = b.tablenick
    AND c.rowguid = b.rowguid;

Предполагаемый план запроса включает в себя информацию о 3 запросах:

  1. Запрос выше
  2. Вызов функции для fn_MSgeneration_downloadonly
  3. Вызов функции для fn_MSArticle_has_downloadonly_property

Фактический план запроса включает только эту информацию:

  1. Запрос выше

Ничего о функциях. Почему информация о функции отсутствует в фактическом плане?

Я попробовал эти варианты:

SET STATISTICS PROFILE ON
SET STATISTICS XML ON

Который создал реальный план, но в нем отсутствовали части 2 и 3, как при использовании опции фактического плана запроса в Management Studio.

Например, если бы я использовал Profiler для сбора информации о вызовах функции, какие события я бы выбрал?


Не нашел ответа, конкретно связанного с планами запросов, но я профилировал SP: StmtStarting и SP: StmtCompleted, и он показал вызовы функций.

Питер
источник

Ответы:

17

И ничего о функциях. Почему информация о функции отсутствует в фактическом плане?

Это сделано из соображений производительности.

Функции, которые содержат BEGINи ENDв определении создают новый фрейм стека T-SQL для каждой входной строки. Другими словами, тело функции выполняется отдельно для каждой строки ввода . Этот факт объясняет большинство проблем с производительностью, связанных со скалярными и многооператорными функциями T-SQL (обратите внимание, что встроенные табличные функции не используют BEGIN...ENDсинтаксис).

В контексте вашего вопроса это приведет к полному SHOWPLANвыводу для каждой строки. Вывод XML-плана довольно многословен и дорог в производстве, поэтому создание полного вывода для каждой строки было бы плохой идеей в общих чертах.

пример

Рассмотрим ниже скалярную функцию T-SQL, созданную в образце базы данных AdventureWorks , которая возвращает имя продукта по его идентификатору:

CREATE FUNCTION dbo.DumbNameLookup
(
    @ProductID integer
)
RETURNS dbo.Name
AS
BEGIN
    RETURN
    (
        SELECT
            p.Name
        FROM Production.Product AS p
        WHERE
            p.ProductID = @ProductID
    );
END;

План предварительного исполнения

План предварительного выполнения (примерный план в SSMS) показывает информацию о плане для родительского оператора и вызовов вложенных функций:

-- Pre-execution plan shows main query and nested function call
SET SHOWPLAN_XML ON;
GO
SELECT dbo.DumbNameLookup(1);
GO
SET SHOWPLAN_XML OFF;

Выход SSMS:

План предварительного исполнения SSMS

Тот же XML, который просматривается в SQL Sentry Plan Explorer, более четко показывает вложенную природу вызовов:

План предварительного исполнения ЧП

Вывод после выполнения

SSMS показывает подробности только для основного запроса, когда запрашивается вывод плана после выполнения:

-- Post-execution plan shows main query only
SET STATISTICS XML ON;
SELECT dbo.DumbNameLookup(1);
SET STATISTICS XML OFF;

Пост-исполнение SSMS

Влияние производительности на выполнение других действий можно продемонстрировать с помощью класса событий XML-профиля Showplan в SQL Server Profiler, используя запрос, который вызывает функцию несколько раз (один раз для каждой входной строки):

SELECT TOP (5)
    p.ProductID,
    dbo.DumbNameLookup(p.ProductID)
FROM Production.Product AS p;

Профилировщик вывода:

Вывод трассировки

Существует пять отдельных планов после выполнения для выполнения функций и один для родительского запроса. Пять функциональных планов выглядят так на нижней панели профилировщика:

Функциональные планы

Родительский план запроса:

Родительский план

Выполнение запроса без TOP (5)предложения приводит к полному плану выполнения для каждой из 504 строк в таблице Product. Вы можете, вероятно, увидеть, как это быстро выйдет из-под контроля с большими столами.

Ситуация для триггеров обратная. Они не показывают никакой информации о плане до выполнения, но включают план после выполнения. Это отражает основанную на множестве природу триггеров; каждый запускается один раз для всех затронутых строк, а не один раз для каждой строки.

Пол Уайт 9
источник
@PaulWhite, есть ли веская причина, по которой планы триггеров не отображаются при запросе приблизительного плана выполнения? Это кажется полезной отсутствующей функцией. Я мог бы создать элемент подключения для него.
USR
@usr - Может быть, потому что фактический выбранный план кэширования может варьироваться в зависимости от фактического количества строк, как описано здесь? technet.microsoft.com/en-us/library/…
Мартин Смит
@MartinSmith, это может быть причиной. Недавно элемент соединения для планов выполнения проверок и ограничений fk был помечен как выполненный, поэтому я надеялся, что они сделают то же самое с триггерами.
USR
@usr - Этот здесь ? 3 месяца? Это должно быть рекордным поворотом для запроса новой функции!
Мартин Смит
@MartinSmith да, тот самый. Это было "исправлено" 1-2 года назад. Я действительно надеюсь, что мне не нужно запрашивать хранилище запросов. Я надеялся нажать кнопку в SSMS. На самом деле, я был удивлен, увидев какое-либо изменение в части двигателя, которая не была затронута годами. Но, возможно, не было ни одного.
USR