Кэширует ли SQL Server результат табличной функции с несколькими операторами?

22

Табличная функция с несколькими утверждениями возвращает свой результат в табличной переменной.

Эти результаты когда-либо повторно используются, или функция всегда полностью оценивается каждый раз, когда она вызывается?

Пол Уайт говорит, что GoFundMonica
источник

Ответы:

23

Результаты табличной функции с несколькими утверждениями (msTVF) никогда не кэшируются и не используются повторно в выражениях (или соединениях), но существует несколько способов повторного использования результата msTVF в одном выражении. В этом смысле msTVF не обязательно повторно заполняется при каждом вызове.

Пример msTVF

Этот (заведомо неэффективный) msTVF возвращает указанный диапазон целых чисел с отметкой времени в каждой строке:

IF OBJECT_ID(N'dbo.IntegerRange', 'TF') IS NOT NULL
    DROP FUNCTION dbo.IntegerRange;
GO
CREATE FUNCTION dbo.IntegerRange (@From integer, @To integer)
RETURNS @T table 
(
    n integer PRIMARY KEY, 
    ts datetime DEFAULT CURRENT_TIMESTAMP
)
WITH SCHEMABINDING
AS
BEGIN
    WHILE @From <= @To
    BEGIN
        INSERT @T (n)
        VALUES (@From);

        SET @From = @From + 1;
    END;
    RETURN;
END;

Переменная статической таблицы

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

SELECT
    IR.n,
    IR.ts 
FROM dbo.IntegerRange(1, 5) AS IR
ORDER BY
    IR.n;

Возвращает результат, похожий на:

Простой результат

План выполнения:

Простой план исполнения

Оператор Sequence сначала вызывает оператор Table Valued Function, который заполняет табличную переменную (обратите внимание, этот оператор не возвращает строк). Затем последовательность вызывает второй вход, который возвращает содержимое табличной переменной (в этом случае используется сканирование кластерного индекса).

Подсказка о том, что в плане используется «статическая» табличная переменная, представляет собой оператор Table Valued Function под Sequence - переменную таблицы необходимо заполнить один раз, прежде чем можно будет приступить к выполнению оставшейся части плана.

Множественный доступ

Чтобы показать, что результат табличной переменной доступен более одного раза, мы будем использовать вторую таблицу со строками, пронумерованными от 1 до 5:

IF OBJECT_ID(N'dbo.T', 'U') IS NOT NULL
    DROP TABLE dbo.T;

CREATE TABLE dbo.T (i integer NOT NULL);

INSERT dbo.T (i) 
VALUES (1), (2), (3), (4), (5);

И новый запрос, который присоединяет эту таблицу к нашей функции (это может быть записано как APPLY):

SELECT T.i,
       IR.n,
       IR.ts
FROM dbo.T AS T
JOIN dbo.IntegerRange(1, 5) AS IR
    ON IR.n = T.i;

Результат:

Присоединиться результат

План выполнения:

Присоединиться к плану

Как и прежде, последовательность заполняет таблицу первой переменной msTVF. Далее, вложенные циклы используются для объединения каждой строки из таблицы Tв строку из результата msTVF. Поскольку в определение функции включен полезный индекс для табличной переменной, можно использовать поиск по индексу.

Ключевым моментом является то, что, когда параметры для msTVF являются константами (включая переменные и параметры) или обрабатываются как константы времени выполнения для оператора механизма выполнения, план будет содержать два отдельных оператора для результата переменной таблицы msTVF: один для заполнения стол; другой - для доступа к результатам, возможно, к таблице несколько раз, и, возможно, к использованию индексов, объявленных в определении функции.

Коррелированные параметры и непостоянные параметры

Чтобы выделить различия, когда используются коррелированные параметры (внешние ссылки) или параметры непостоянных функций, мы изменим содержимое таблицы, Tчтобы у функции было гораздо больше работы:

TRUNCATE TABLE dbo.T;

INSERT dbo.T (i) 
VALUES (50001), (50002), (50003), (50004), (50005);

Следующий модифицированный запрос теперь использует внешнюю ссылку на таблицу Tв одном из параметров функции:

SELECT T.i,
       IR.n,
       IR.ts
FROM dbo.T AS T
CROSS APPLY dbo.IntegerRange(1, T.i) AS IR
WHERE IR.n = T.i;

Этот запрос занимает около 8 секунд, чтобы вернуть результаты, как:

Коррелированный результат

Обратите внимание на разницу во времени между строками в столбце ts. Предложение WHEREограничивает конечный результат для вывода разумного размера, но неэффективной функции все еще требуется некоторое время, чтобы заполнить табличную переменную 50 000 нечетными строками (в зависимости от коррелированного значения iиз таблицы T).

План выполнения:

Коррелированный план выполнения

Обратите внимание на отсутствие оператора Sequence. Теперь есть единственный оператор Table Valued Function, который заполняет табличную переменную и возвращает ее строки на каждой итерации объединения вложенных циклов.

Чтобы быть понятным: всего 5 строк в таблице T, оператор Table Valued Function выполняется 5 раз. Он генерирует 50,001 строк на первой итерации, 50,002 на второй ... и так далее. Переменная таблицы «выбрасывается» (усекается) между итерациями, поэтому каждый из пяти вызовов является полной популяцией. Вот почему это так медленно, и каждая строка занимает примерно одно и то же время, чтобы появиться в результате.

Примечания стороны:

Естественно, вышеописанный сценарий намеренно придуман, чтобы показать, насколько низкой может быть производительность, когда msTVF заполняет много строк на каждой итерации.

Разумное осуществление вышеуказанного кода будет установить оба параметр msTVF к i, и удалить избыточное WHEREположение. Переменная таблицы по-прежнему будет обрезаться и повторно заполняться на каждой итерации, но только с одной строкой каждый раз.

Мы также могли бы извлечь минимальные и максимальные iзначения из Tи сохранить их в переменных на предыдущем шаге. Вызов функции с переменными вместо коррелированных параметров позволит использовать шаблон статических переменных таблицы, как отмечалось ранее.

Кэширование для неизмененных коррелированных параметров

Возвращаясь к исходному вопросу еще раз, когда статический шаблон Sequence не может быть использован, SQL Server может избежать усечения и повторного заполнения табличной переменной msTVF, если ни один из коррелированных параметров не изменился со времени предыдущей итерации соединения с вложенным циклом.

Чтобы продемонстрировать это, мы заменим содержимое на Tпять одинаковых i значений:

TRUNCATE TABLE dbo.T;

INSERT dbo.T (i) 
VALUES (50005), (50005), (50005), (50005), (50005);

Снова запрос с коррелированным параметром:

SELECT T.i,
       IR.n,
       IR.ts
FROM dbo.T AS T
CROSS APPLY dbo.IntegerRange(1, T.i) AS IR
WHERE IR.n = T.i;

На этот раз результаты появятся примерно через 1,5 секунды :

Идентичные результаты строки

Обратите внимание на одинаковые временные метки в каждом ряду. Кэшированный результат в табличной переменной повторно используется для последующих итераций, где коррелированное значение iне изменяется. Повторное использование результата намного быстрее, чем вставка 50,005 строк каждый раз.

План выполнения выглядит очень похоже на ранее:

План для одинаковых строк

Основное различие заключается в свойствах Actual Rebinds и Actual Rewinds оператора Table Valued Function:

Свойства оператора

Когда коррелированные параметры не изменяются, SQL Server может воспроизвести (перемотать) текущие результаты в табличной переменной. Когда корреляция изменится, SQL Server должен усечь и снова заполнить табличную переменную (перепривязать). Одно повторное связывание происходит на первой итерации; все четыре последующие итерации перематываются, так как значение T.iне изменяется.

Пол Уайт говорит, что GoFundMonica
источник